2 * ssa.c: Static single assign form support for the JIT compiler.
5 * Dietmar Maurer (dietmar@ximian.com)
7 * (C) 2003 Ximian, Inc.
8 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
13 #include <mono/metadata/debug-helpers.h>
14 #include <mono/metadata/mempool.h>
15 #include <mono/metadata/mempool-internals.h>
24 #define CREATE_PRUNED_SSA
28 #define NEW_PHI(cfg,dest,val) do { \
29 MONO_INST_NEW ((cfg), (dest), OP_PHI); \
30 (dest)->inst_c0 = (val); \
39 unlink_target (MonoBasicBlock
*bb
, MonoBasicBlock
*target
)
43 for (i
= 0; i
< bb
->out_count
; i
++) {
44 if (bb
->out_bb
[i
] == target
) {
45 bb
->out_bb
[i
] = bb
->out_bb
[--bb
->out_count
];
49 for (i
= 0; i
< target
->in_count
; i
++) {
50 if (target
->in_bb
[i
] == bb
) {
51 target
->in_bb
[i
] = target
->in_bb
[--target
->in_count
];
59 unlink_unused_bblocks (MonoCompile
*cfg
)
64 g_assert (cfg
->comp_done
& MONO_COMP_REACHABILITY
);
66 if (G_UNLIKELY (cfg
->verbose_level
> 1))
67 printf ("\nUNLINK UNUSED BBLOCKS:\n");
69 for (bb
= cfg
->bb_entry
; bb
&& bb
->next_bb
;) {
70 if (!(bb
->next_bb
->flags
& BB_REACHABLE
)) {
71 bb
->next_bb
= bb
->next_bb
->next_bb
;
76 for (i
= 1; i
< cfg
->num_bblocks
; i
++) {
77 bb
= cfg
->bblocks
[i
];
79 if (!(bb
->flags
& BB_REACHABLE
)) {
80 for (j
= 0; j
< bb
->in_count
; j
++) {
81 unlink_target (bb
->in_bb
[j
], bb
);
83 for (j
= 0; j
< bb
->out_count
; j
++) {
84 unlink_target (bb
, bb
->out_bb
[j
]);
86 if (G_UNLIKELY (cfg
->verbose_level
> 1))
87 printf ("\tUnlinked BB%d\n", bb
->block_num
);
94 * remove_bb_from_phis:
96 * Remove BB from the PHI statements in TARGET.
99 remove_bb_from_phis (MonoCompile
*cfg
, MonoBasicBlock
*bb
, MonoBasicBlock
*target
)
104 for (i
= 0; i
< target
->in_count
; i
++) {
105 if (target
->in_bb
[i
] == bb
) {
109 g_assert (i
< target
->in_count
);
111 for (ins
= target
->code
; ins
; ins
= ins
->next
) {
112 if (MONO_IS_PHI (ins
)) {
113 for (j
= i
; j
< ins
->inst_phi_args
[0] - 1; ++j
)
114 ins
->inst_phi_args
[j
+ 1] = ins
->inst_phi_args
[j
+ 2];
115 ins
->inst_phi_args
[0] --;
123 op_phi_to_move (int opcode
)
135 g_assert_not_reached ();
142 record_use (MonoCompile
*cfg
, MonoInst
*var
, MonoBasicBlock
*bb
, MonoInst
*ins
)
145 MonoVarUsageInfo
*ui
= (MonoVarUsageInfo
*)mono_mempool_alloc (cfg
->mempool
, sizeof (MonoVarUsageInfo
));
147 info
= MONO_VARINFO (cfg
, var
->inst_c0
);
151 info
->uses
= g_list_prepend_mempool (cfg
->mempool
, info
->uses
, ui
);
160 * mono_ssa_rename_vars:
162 * Implement renaming of SSA variables. Also compute def-use information in parallel.
163 * @stack_history points to an area of memory which can be used for storing changes
164 * made to the stack, so they can be reverted later.
167 mono_ssa_rename_vars (MonoCompile
*cfg
, int max_vars
, MonoBasicBlock
*bb
, gboolean
*originals_used
, MonoInst
**stack
, guint32
*lvreg_stack
, gboolean
*lvreg_defined
, RenameInfo
*stack_history
, int stack_history_size
)
169 MonoInst
*ins
, *new_var
;
172 int stack_history_len
= 0;
174 if (cfg
->verbose_level
>= 4)
175 printf ("\nRENAME VARS BLOCK %d:\n", bb
->block_num
);
177 /* First pass: Create new vars */
178 for (ins
= bb
->code
; ins
; ins
= ins
->next
) {
179 const char *spec
= INS_INFO (ins
->opcode
);
181 int sregs
[MONO_MAX_SRC_REGS
];
184 printf ("\tProcessing "); mono_print_ins (ins
);
186 if (ins
->opcode
== OP_NOP
)
190 num_sregs
= mono_inst_get_src_registers (ins
, sregs
);
191 for (i
= 0; i
< num_sregs
; ++i
) {
192 if (spec
[MONO_INST_SRC1
+ i
] != ' ') {
193 MonoInst
*var
= get_vreg_to_inst (cfg
, sregs
[i
]);
194 if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
))) {
195 int idx
= var
->inst_c0
;
197 if (var
->opcode
!= OP_ARG
)
198 g_assert (stack
[idx
]);
199 sregs
[i
] = stack
[idx
]->dreg
;
200 record_use (cfg
, stack
[idx
], bb
, ins
);
203 record_use (cfg
, var
, bb
, ins
);
205 else if (G_UNLIKELY (!var
&& lvreg_stack
[sregs
[i
]]))
206 sregs
[i
] = lvreg_stack
[sregs
[i
]];
209 mono_inst_set_src_registers (ins
, sregs
);
211 if (MONO_IS_STORE_MEMBASE (ins
)) {
212 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->dreg
);
213 if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
))) {
214 int idx
= var
->inst_c0
;
216 if (var
->opcode
!= OP_ARG
)
217 g_assert (stack
[idx
]);
218 ins
->dreg
= stack
[idx
]->dreg
;
219 record_use (cfg
, stack
[idx
], bb
, ins
);
222 record_use (cfg
, var
, bb
, ins
);
224 else if (G_UNLIKELY (!var
&& lvreg_stack
[ins
->dreg
]))
225 ins
->dreg
= lvreg_stack
[ins
->dreg
];
229 if ((spec
[MONO_INST_DEST
] != ' ') && !MONO_IS_STORE_MEMBASE (ins
)) {
230 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->dreg
);
233 if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
))) {
235 g_assert (idx
< max_vars
);
237 if (var
->opcode
== OP_ARG
)
238 originals_used
[idx
] = TRUE
;
241 g_assert (stack_history_len
< stack_history_size
);
242 stack_history
[stack_history_len
].var
= stack
[idx
];
243 stack_history
[stack_history_len
].idx
= idx
;
244 stack_history_len
++;
246 if (originals_used
[idx
]) {
247 new_var
= mono_compile_create_var (cfg
, var
->inst_vtype
, OP_LOCAL
);
248 new_var
->flags
= var
->flags
;
249 MONO_VARINFO (cfg
, new_var
->inst_c0
)->reg
= idx
;
251 if (cfg
->verbose_level
>= 4)
252 printf (" R%d -> R%d\n", var
->dreg
, new_var
->dreg
);
254 stack
[idx
] = new_var
;
256 ins
->dreg
= new_var
->dreg
;
261 originals_used
[idx
] = TRUE
;
264 info
= MONO_VARINFO (cfg
, var
->inst_c0
);
268 else if (G_UNLIKELY (!var
&& lvreg_defined
[ins
->dreg
] && (ins
->dreg
>= MONO_MAX_IREGS
))) {
269 /* Perform renaming for local vregs */
270 lvreg_stack
[ins
->dreg
] = vreg_is_ref (cfg
, ins
->dreg
) ? mono_alloc_ireg_ref (cfg
) : mono_alloc_preg (cfg
);
271 ins
->dreg
= lvreg_stack
[ins
->dreg
];
274 lvreg_defined
[ins
->dreg
] = TRUE
;
278 printf ("\tAfter processing "); mono_print_ins (ins
);
283 /* Rename PHI arguments in succeeding bblocks */
284 for (i
= 0; i
< bb
->out_count
; i
++) {
285 MonoBasicBlock
*n
= bb
->out_bb
[i
];
287 for (j
= 0; j
< n
->in_count
; j
++)
288 if (n
->in_bb
[j
] == bb
)
291 for (ins
= n
->code
; ins
; ins
= ins
->next
) {
292 if (MONO_IS_PHI (ins
)) {
295 new_var
= stack
[idx
];
297 new_var
= cfg
->varinfo
[idx
];
299 printf ("FOUND PHI %d (%d, %d)\n", idx
, j
, new_var
->inst_c0
);
301 ins
->inst_phi_args
[j
+ 1] = new_var
->dreg
;
302 record_use (cfg
, new_var
, n
, ins
);
303 if (G_UNLIKELY (cfg
->verbose_level
>= 4))
304 printf ("\tAdd PHI R%d <- R%d to BB%d\n", ins
->dreg
, new_var
->dreg
, n
->block_num
);
307 /* The phi nodes are at the beginning of the bblock */
313 for (tmp
= bb
->dominated
; tmp
; tmp
= tmp
->next
) {
314 mono_ssa_rename_vars (cfg
, max_vars
, (MonoBasicBlock
*)tmp
->data
, originals_used
, stack
, lvreg_stack
, lvreg_defined
, stack_history
+ stack_history_len
, stack_history_size
- stack_history_len
);
319 for (i
= stack_history_len
- 1; i
>= 0; i
--) {
320 stack
[stack_history
[i
].idx
] = stack_history
[i
].var
;
323 cfg
->comp_done
|= MONO_COMP_SSA_DEF_USE
;
327 mono_ssa_compute (MonoCompile
*cfg
)
329 int i
, j
, idx
, bitsize
;
331 MonoMethodVar
*vinfo
= g_new0 (MonoMethodVar
, cfg
->num_varinfo
);
332 MonoInst
*ins
, **stack
;
333 guint8
*buf
, *buf_start
;
334 RenameInfo
*stack_history
;
335 int stack_history_size
;
337 guint32
*lvreg_stack
;
338 gboolean
*lvreg_defined
;
340 g_assert (!(cfg
->comp_done
& MONO_COMP_SSA
));
342 g_assert (!cfg
->disable_ssa
);
344 if (cfg
->verbose_level
>= 4)
345 printf ("\nCOMPUTE SSA %d (R%d-)\n\n", cfg
->num_varinfo
, cfg
->next_vreg
);
347 #ifdef CREATE_PRUNED_SSA
348 /* we need liveness for pruned SSA */
349 if (!(cfg
->comp_done
& MONO_COMP_LIVENESS
))
350 mono_analyze_liveness (cfg
);
353 mono_compile_dominator_info (cfg
, MONO_COMP_DOM
| MONO_COMP_IDOM
| MONO_COMP_DFRONTIER
);
355 bitsize
= mono_bitset_alloc_size (cfg
->num_bblocks
, 0);
356 buf
= buf_start
= (guint8
*)g_malloc0 (mono_bitset_alloc_size (cfg
->num_bblocks
, 0) * cfg
->num_varinfo
);
358 for (i
= 0; i
< cfg
->num_varinfo
; ++i
) {
359 vinfo
[i
].def_in
= mono_bitset_mem_new (buf
, cfg
->num_bblocks
, 0);
362 /* implicit reference at start */
363 if (cfg
->varinfo
[i
]->opcode
== OP_ARG
)
364 mono_bitset_set_fast (vinfo
[i
].def_in
, 0);
367 for (i
= 0; i
< cfg
->num_bblocks
; ++i
) {
368 MONO_BB_FOR_EACH_INS (cfg
->bblocks
[i
], ins
) {
369 if (ins
->opcode
== OP_NOP
)
372 if (!MONO_IS_STORE_MEMBASE (ins
) && get_vreg_to_inst (cfg
, ins
->dreg
)) {
373 mono_bitset_set_fast (vinfo
[get_vreg_to_inst (cfg
, ins
->dreg
)->inst_c0
].def_in
, i
);
378 /* insert phi functions */
379 for (i
= 0; i
< cfg
->num_varinfo
; ++i
) {
380 MonoInst
*var
= cfg
->varinfo
[i
];
382 #if SIZEOF_REGISTER == 4
383 if (var
->type
== STACK_I8
&& !COMPILE_LLVM (cfg
))
386 if (var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
))
389 /* Most variables have only one definition */
390 if (mono_bitset_count (vinfo
[i
].def_in
) <= 1)
393 set
= mono_compile_iterated_dfrontier (cfg
, vinfo
[i
].def_in
);
395 if (cfg
->verbose_level
>= 4) {
396 if (mono_bitset_count (set
) > 0) {
397 printf ("\tR%d needs PHI functions in ", var
->dreg
);
398 mono_blockset_print (cfg
, set
, "", -1);
402 mono_bitset_foreach_bit (set
, idx
, cfg
->num_bblocks
) {
403 MonoBasicBlock
*bb
= cfg
->bblocks
[idx
];
405 /* fixme: create pruned SSA? we would need liveness information for that */
407 if (bb
== cfg
->bb_exit
&& !COMPILE_LLVM (cfg
))
410 if ((cfg
->comp_done
& MONO_COMP_LIVENESS
) && !mono_bitset_test_fast (bb
->live_in_set
, i
)) {
411 //printf ("%d is not live in BB%d %s\n", i, bb->block_num, mono_method_full_name (cfg->method, TRUE));
415 NEW_PHI (cfg
, ins
, i
);
423 ins
->opcode
= OP_PHI
;
426 ins
->opcode
= OP_FPHI
;
429 ins
->opcode
= MONO_CLASS_IS_SIMD (cfg
, var
->klass
) ? OP_XPHI
: OP_VPHI
;
433 if (var
->inst_vtype
->byref
)
434 ins
->klass
= mono_defaults
.int_class
;
436 ins
->klass
= var
->klass
;
438 ins
->inst_phi_args
= (int *)mono_mempool_alloc0 (cfg
->mempool
, sizeof (int) * (cfg
->bblocks
[idx
]->in_count
+ 1));
439 ins
->inst_phi_args
[0] = cfg
->bblocks
[idx
]->in_count
;
442 for (j
= 0; j
< cfg
->bblocks
[idx
]->in_count
; ++j
)
443 ins
->inst_phi_args
[j
+ 1] = -1;
445 ins
->dreg
= cfg
->varinfo
[i
]->dreg
;
447 mono_bblock_insert_before_ins (bb
, bb
->code
, ins
);
450 printf ("ADD PHI BB%d %s\n", cfg
->bblocks
[idx
]->block_num
, mono_method_full_name (cfg
->method
, TRUE
));
461 stack
= (MonoInst
**)alloca (sizeof (MonoInst
*) * cfg
->num_varinfo
);
462 memset (stack
, 0, sizeof (MonoInst
*) * cfg
->num_varinfo
);
464 lvreg_stack
= g_new0 (guint32
, cfg
->next_vreg
);
465 lvreg_defined
= g_new0 (gboolean
, cfg
->next_vreg
);
466 stack_history_size
= 10240;
467 stack_history
= g_new (RenameInfo
, stack_history_size
);
468 originals
= g_new0 (gboolean
, cfg
->num_varinfo
);
469 mono_ssa_rename_vars (cfg
, cfg
->num_varinfo
, cfg
->bb_entry
, originals
, stack
, lvreg_stack
, lvreg_defined
, stack_history
, stack_history_size
);
470 g_free (stack_history
);
472 g_free (lvreg_stack
);
473 g_free (lvreg_defined
);
475 if (cfg
->verbose_level
>= 4)
476 printf ("\nEND COMPUTE SSA.\n\n");
478 cfg
->comp_done
|= MONO_COMP_SSA
;
482 * mono_ssa_remove_gsharedvt:
484 * Same as mono_ssa_remove, but only remove phi nodes for gsharedvt variables.
487 mono_ssa_remove_gsharedvt (MonoCompile
*cfg
)
489 MonoInst
*ins
, *var
, *move
;
493 * When compiling gsharedvt code, we need to get rid of the VPHI instructions,
494 * since they cannot be handled later in the llvm backend.
496 g_assert (cfg
->comp_done
& MONO_COMP_SSA
);
498 for (i
= 0; i
< cfg
->num_bblocks
; ++i
) {
499 MonoBasicBlock
*bb
= cfg
->bblocks
[i
];
501 if (cfg
->verbose_level
>= 4)
502 printf ("\nREMOVE SSA %d:\n", bb
->block_num
);
504 for (ins
= bb
->code
; ins
; ins
= ins
->next
) {
505 if (!(MONO_IS_PHI (ins
) && ins
->opcode
== OP_VPHI
&& mini_is_gsharedvt_variable_type (&ins
->klass
->byval_arg
)))
508 g_assert (ins
->inst_phi_args
[0] == bb
->in_count
);
509 var
= get_vreg_to_inst (cfg
, ins
->dreg
);
511 /* Check for PHI nodes where all the inputs are the same */
512 first
= ins
->inst_phi_args
[1];
514 for (j
= 1; j
< bb
->in_count
; ++j
)
515 if (first
!= ins
->inst_phi_args
[j
+ 1])
518 if ((bb
->in_count
> 1) && (j
== bb
->in_count
)) {
519 ins
->opcode
= op_phi_to_move (ins
->opcode
);
520 if (ins
->opcode
== OP_VMOVE
)
521 g_assert (ins
->klass
);
524 for (j
= 0; j
< bb
->in_count
; j
++) {
525 MonoBasicBlock
*pred
= bb
->in_bb
[j
];
526 int sreg
= ins
->inst_phi_args
[j
+ 1];
528 if (cfg
->verbose_level
>= 4)
529 printf ("\tADD R%d <- R%d in BB%d\n", var
->dreg
, sreg
, pred
->block_num
);
530 if (var
->dreg
!= sreg
) {
531 MONO_INST_NEW (cfg
, move
, op_phi_to_move (ins
->opcode
));
532 if (move
->opcode
== OP_VMOVE
) {
533 g_assert (ins
->klass
);
534 move
->klass
= ins
->klass
;
536 move
->dreg
= var
->dreg
;
538 mono_add_ins_to_end (pred
, move
);
549 mono_ssa_remove (MonoCompile
*cfg
)
551 MonoInst
*ins
, *var
, *move
;
552 int bbindex
, i
, j
, first
;
554 g_assert (cfg
->comp_done
& MONO_COMP_SSA
);
556 for (i
= 0; i
< cfg
->num_bblocks
; ++i
) {
557 MonoBasicBlock
*bb
= cfg
->bblocks
[i
];
559 if (cfg
->verbose_level
>= 4)
560 printf ("\nREMOVE SSA %d:\n", bb
->block_num
);
562 for (ins
= bb
->code
; ins
; ins
= ins
->next
) {
563 if (MONO_IS_PHI (ins
)) {
564 g_assert (ins
->inst_phi_args
[0] == bb
->in_count
);
565 var
= get_vreg_to_inst (cfg
, ins
->dreg
);
567 /* Check for PHI nodes where all the inputs are the same */
568 first
= ins
->inst_phi_args
[1];
570 for (j
= 1; j
< bb
->in_count
; ++j
)
571 if (first
!= ins
->inst_phi_args
[j
+ 1])
574 if ((bb
->in_count
> 1) && (j
== bb
->in_count
)) {
575 ins
->opcode
= op_phi_to_move (ins
->opcode
);
576 if (ins
->opcode
== OP_VMOVE
)
577 g_assert (ins
->klass
);
580 for (j
= 0; j
< bb
->in_count
; j
++) {
581 MonoBasicBlock
*pred
= bb
->in_bb
[j
];
582 int sreg
= ins
->inst_phi_args
[j
+ 1];
584 if (cfg
->verbose_level
>= 4)
585 printf ("\tADD R%d <- R%d in BB%d\n", var
->dreg
, sreg
, pred
->block_num
);
586 if (var
->dreg
!= sreg
) {
587 MONO_INST_NEW (cfg
, move
, op_phi_to_move (ins
->opcode
));
588 if (move
->opcode
== OP_VMOVE
) {
589 g_assert (ins
->klass
);
590 move
->klass
= ins
->klass
;
592 move
->dreg
= var
->dreg
;
594 mono_add_ins_to_end (pred
, move
);
604 if (cfg
->verbose_level
>= 4) {
605 for (i
= 0; i
< cfg
->num_bblocks
; ++i
) {
606 MonoBasicBlock
*bb
= cfg
->bblocks
[i
];
608 mono_print_bb (bb
, "AFTER REMOVE SSA:");
613 * Removal of SSA form introduces many copies. To avoid this, we tyry to coalesce
614 * the variables if possible. Since the newly introduced SSA variables don't
615 * have overlapping live ranges (because we don't do agressive optimization), we
616 * can coalesce them into the original variable.
619 for (bbindex
= 0; bbindex
< cfg
->num_bblocks
; ++bbindex
) {
620 MonoBasicBlock
*bb
= cfg
->bblocks
[bbindex
];
622 for (ins
= bb
->code
; ins
; ins
= ins
->next
) {
623 const char *spec
= INS_INFO (ins
->opcode
);
625 int sregs
[MONO_MAX_SRC_REGS
];
627 if (ins
->opcode
== OP_NOP
)
630 if (spec
[MONO_INST_DEST
] != ' ') {
631 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->dreg
);
634 MonoMethodVar
*vmv
= MONO_VARINFO (cfg
, var
->inst_c0
);
637 * The third condition avoids coalescing with variables eliminated
640 if ((vmv
->reg
!= -1) && (vmv
->idx
!= vmv
->reg
) && (MONO_VARINFO (cfg
, vmv
->reg
)->reg
!= -1)) {
641 printf ("COALESCE: R%d -> R%d\n", ins
->dreg
, cfg
->varinfo
[vmv
->reg
]->dreg
);
642 ins
->dreg
= cfg
->varinfo
[vmv
->reg
]->dreg
;
647 num_sregs
= mono_inst_get_src_registers (ins
, sregs
);
648 for (i
= 0; i
< num_sregs
; ++i
) {
649 MonoInst
*var
= get_vreg_to_inst (cfg
, sregs
[i
]);
652 MonoMethodVar
*vmv
= MONO_VARINFO (cfg
, var
->inst_c0
);
654 if ((vmv
->reg
!= -1) && (vmv
->idx
!= vmv
->reg
) && (MONO_VARINFO (cfg
, vmv
->reg
)->reg
!= -1)) {
655 printf ("COALESCE: R%d -> R%d\n", sregs
[i
], cfg
->varinfo
[vmv
->reg
]->dreg
);
656 sregs
[i
] = cfg
->varinfo
[vmv
->reg
]->dreg
;
660 mono_inst_set_src_registers (ins
, sregs
);
664 for (i
= 0; i
< cfg
->num_varinfo
; ++i
) {
665 MONO_VARINFO (cfg
, i
)->reg
= -1;
668 if (cfg
->comp_done
& MONO_COMP_REACHABILITY
)
669 unlink_unused_bblocks (cfg
);
671 cfg
->comp_done
&= ~MONO_COMP_LIVENESS
;
673 cfg
->comp_done
&= ~MONO_COMP_SSA
;
677 mono_ssa_create_def_use (MonoCompile
*cfg
)
683 g_assert (!(cfg
->comp_done
& MONO_COMP_SSA_DEF_USE
));
685 for (bb
= cfg
->bb_entry
; bb
; bb
= bb
->next_bb
) {
686 for (ins
= bb
->code
; ins
; ins
= ins
->next
) {
687 const char *spec
= INS_INFO (ins
->opcode
);
690 int sregs
[MONO_MAX_SRC_REGS
];
692 if (ins
->opcode
== OP_NOP
)
696 num_sregs
= mono_inst_get_src_registers (ins
, sregs
);
697 for (i
= 0; i
< num_sregs
; ++i
) {
698 MonoInst
*var
= get_vreg_to_inst (cfg
, sregs
[i
]);
699 if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
)))
700 record_use (cfg
, var
, bb
, ins
);
703 if (MONO_IS_STORE_MEMBASE (ins
)) {
704 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->dreg
);
705 if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
)))
706 record_use (cfg
, var
, bb
, ins
);
709 if (MONO_IS_PHI (ins
)) {
710 for (i
= ins
->inst_phi_args
[0]; i
> 0; i
--) {
711 g_assert (ins
->inst_phi_args
[i
] != -1);
712 record_use (cfg
, get_vreg_to_inst (cfg
, ins
->inst_phi_args
[i
]), bb
, ins
);
717 if ((spec
[MONO_INST_DEST
] != ' ') && !MONO_IS_STORE_MEMBASE (ins
)) {
718 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->dreg
);
720 if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
))) {
721 info
= MONO_VARINFO (cfg
, var
->inst_c0
);
729 cfg
->comp_done
|= MONO_COMP_SSA_DEF_USE
;
733 mono_ssa_copyprop (MonoCompile
*cfg
)
738 g_assert ((cfg
->comp_done
& MONO_COMP_SSA_DEF_USE
));
740 for (index
= 0; index
< cfg
->num_varinfo
; ++index
) {
741 MonoInst
*var
= cfg
->varinfo
[index
];
742 MonoMethodVar
*info
= MONO_VARINFO (cfg
, index
);
744 if (info
->def
&& (MONO_IS_MOVE (info
->def
))) {
745 MonoInst
*var2
= get_vreg_to_inst (cfg
, info
->def
->sreg1
);
747 if (var2
&& !(var2
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
)) && MONO_VARINFO (cfg
, var2
->inst_c0
)->def
&& (!MONO_IS_PHI (MONO_VARINFO (cfg
, var2
->inst_c0
)->def
))) {
748 /* Rewrite all uses of var to be uses of var2 */
749 int dreg
= var
->dreg
;
750 int sreg1
= var2
->dreg
;
754 MonoVarUsageInfo
*u
= (MonoVarUsageInfo
*)l
->data
;
755 MonoInst
*ins
= u
->inst
;
756 GList
*next
= l
->next
;
758 int sregs
[MONO_MAX_SRC_REGS
];
760 num_sregs
= mono_inst_get_src_registers (ins
, sregs
);
761 for (i
= 0; i
< num_sregs
; ++i
) {
762 if (sregs
[i
] == dreg
)
766 g_assert (sregs
[i
] == dreg
);
768 mono_inst_set_src_registers (ins
, sregs
);
769 } else if (MONO_IS_STORE_MEMBASE (ins
) && ins
->dreg
== dreg
) {
771 } else if (MONO_IS_PHI (ins
)) {
772 for (i
= ins
->inst_phi_args
[0]; i
> 0; i
--) {
773 int sreg
= ins
->inst_phi_args
[i
];
774 if (sreg
== var
->dreg
)
778 ins
->inst_phi_args
[i
] = sreg1
;
781 g_assert_not_reached ();
783 record_use (cfg
, var2
, u
->bb
, ins
);
793 if (cfg
->verbose_level
>= 4) {
796 for (bb
= cfg
->bb_entry
; bb
; bb
= bb
->next_bb
)
797 mono_print_bb (bb
, "AFTER SSA COPYPROP");
802 evaluate_ins (MonoCompile
*cfg
, MonoInst
*ins
, MonoInst
**res
, MonoInst
**carray
)
804 MonoInst
*args
[MONO_MAX_SRC_REGS
];
805 int rs
[MONO_MAX_SRC_REGS
];
807 gboolean const_args
= TRUE
;
808 const char *spec
= INS_INFO (ins
->opcode
);
810 int sregs
[MONO_MAX_SRC_REGS
];
812 /* Short-circuit this */
813 if (ins
->opcode
== OP_ICONST
) {
818 if (ins
->opcode
== OP_NOP
)
821 num_sregs
= mono_inst_get_src_registers (ins
, sregs
);
822 for (i
= 0; i
< MONO_MAX_SRC_REGS
; ++i
)
824 for (i
= 0; i
< num_sregs
; ++i
) {
825 MonoInst
*var
= get_vreg_to_inst (cfg
, sregs
[i
]);
828 args
[i
] = carray
[sregs
[i
]];
831 else if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
)))
832 rs
[i
] = MONO_VARINFO (cfg
, var
->inst_c0
)->cpstate
;
839 if (num_sregs
> 0 && const_args
) {
840 g_assert (num_sregs
<= 2);
841 if ((spec
[MONO_INST_DEST
] != ' ') && carray
[ins
->dreg
]) {
843 *res
= carray
[ins
->dreg
];
846 c0
= mono_constant_fold_ins (cfg
, ins
, args
[0], args
[1], FALSE
);
848 if (G_UNLIKELY (cfg
->verbose_level
> 1)) {
849 printf ("\t cfold -> ");
856 /* Can't cfold this ins */
862 for (i
= 0; i
< num_sregs
; ++i
) {
870 change_varstate (MonoCompile
*cfg
, GList
**cvars
, MonoMethodVar
*info
, int state
, MonoInst
*c0
, MonoInst
**carray
)
872 if (info
->cpstate
>= state
)
875 info
->cpstate
= state
;
877 if (G_UNLIKELY (cfg
->verbose_level
> 1))
878 printf ("\tState of R%d set to %d\n", cfg
->varinfo
[info
->idx
]->dreg
, info
->cpstate
);
883 carray
[cfg
->varinfo
[info
->idx
]->dreg
] = c0
;
885 if (!g_list_find (*cvars
, info
)) {
886 *cvars
= g_list_prepend (*cvars
, info
);
891 add_cprop_bb (MonoCompile
*cfg
, MonoBasicBlock
*bb
, GList
**bblist
)
893 if (G_UNLIKELY (cfg
->verbose_level
> 1))
894 printf ("\tAdd BB%d to worklist\n", bb
->block_num
);
896 if (!(bb
->flags
& BB_REACHABLE
)) {
897 bb
->flags
|= BB_REACHABLE
;
898 *bblist
= g_list_prepend (*bblist
, bb
);
903 visit_inst (MonoCompile
*cfg
, MonoBasicBlock
*bb
, MonoInst
*ins
, GList
**cvars
, GList
**bblist
, MonoInst
**carray
)
905 const char *spec
= INS_INFO (ins
->opcode
);
907 if (ins
->opcode
== OP_NOP
)
910 if (cfg
->verbose_level
> 1)
911 mono_print_ins (ins
);
913 /* FIXME: Support longs/floats */
914 /* FIXME: Work on vregs as well */
916 if (MONO_IS_PHI (ins
)) {
917 MonoMethodVar
*info
= MONO_VARINFO (cfg
, get_vreg_to_inst (cfg
, ins
->dreg
)->inst_c0
);
921 for (j
= 1; j
<= ins
->inst_phi_args
[0]; j
++) {
922 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->inst_phi_args
[j
]);
923 MonoMethodVar
*mv
= MONO_VARINFO (cfg
, var
->inst_c0
);
924 MonoInst
*src
= mv
->def
;
926 if (mv
->def_bb
&& !(mv
->def_bb
->flags
& BB_REACHABLE
))
929 if (!mv
->def
|| !src
|| mv
->cpstate
== 2) {
930 change_varstate (cfg
, cvars
, info
, 2, NULL
, carray
);
934 if (mv
->cpstate
== 0)
937 g_assert (carray
[var
->dreg
]);
940 c0
= carray
[var
->dreg
];
943 if (c0
->opcode
!= OP_ICONST
) {
944 change_varstate (cfg
, cvars
, info
, 2, NULL
, carray
);
948 if (carray
[var
->dreg
]->inst_c0
!= c0
->inst_c0
) {
949 change_varstate (cfg
, cvars
, info
, 2, NULL
, carray
);
954 if (c0
&& info
->cpstate
< 1) {
955 change_varstate (cfg
, cvars
, info
, 1, c0
, carray
);
957 g_assert (c0
->opcode
== OP_ICONST
);
960 else if (!MONO_IS_STORE_MEMBASE (ins
) && ((spec
[MONO_INST_SRC1
] != ' ') || (spec
[MONO_INST_SRC2
] != ' ') || (spec
[MONO_INST_DEST
] != ' '))) {
964 if (spec
[MONO_INST_DEST
] != ' ')
965 var
= get_vreg_to_inst (cfg
, ins
->dreg
);
970 state
= evaluate_ins (cfg
, ins
, &c0
, carray
);
972 if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
))) {
973 MonoMethodVar
*info
= MONO_VARINFO (cfg
, var
->inst_c0
);
975 if (info
->cpstate
< 2) {
977 change_varstate (cfg
, cvars
, info
, 1, c0
, carray
);
979 change_varstate (cfg
, cvars
, info
, 2, NULL
, carray
);
982 else if (!var
&& (ins
->dreg
!= -1)) {
984 * We don't record def-use information for local vregs since it would be
985 * expensive. Instead, we depend on the fact that all uses of the vreg are in
986 * the same bblock, so they will be examined after the definition.
987 * FIXME: This isn't true if the ins is visited through an SSA edge.
990 carray
[ins
->dreg
] = c0
;
992 if (carray
[ins
->dreg
]) {
994 * The state of the vreg changed from constant to non-constant
995 * -> need to rescan the whole bblock.
997 carray
[ins
->dreg
] = NULL
;
998 /* FIXME: Speed this up */
1000 if (!g_list_find (*bblist
, bb
))
1001 *bblist
= g_list_prepend (*bblist
, bb
);
1006 if (MONO_IS_JUMP_TABLE (ins
)) {
1008 MonoJumpInfoBBTable
*table
= (MonoJumpInfoBBTable
*)MONO_JUMP_TABLE_FROM_INS (ins
);
1010 if (!ins
->next
|| ins
->next
->opcode
!= OP_PADD
) {
1011 /* The PADD was optimized away */
1012 /* FIXME: handle this as well */
1013 for (i
= 0; i
< table
->table_size
; i
++)
1014 if (table
->table
[i
])
1015 add_cprop_bb (cfg
, table
->table
[i
], bblist
);
1019 g_assert (ins
->next
->opcode
== OP_PADD
);
1020 g_assert (ins
->next
->sreg1
== ins
->dreg
);
1022 if (carray
[ins
->next
->sreg2
]) {
1023 #if SIZEOF_REGISTER == 8
1024 int idx
= carray
[ins
->next
->sreg2
]->inst_c0
>> 3;
1026 int idx
= carray
[ins
->next
->sreg2
]->inst_c0
>> 2;
1028 if ((idx
< 0) || (idx
>= table
->table_size
))
1029 /* Out-of-range, no branch is executed */
1032 if (table
->table
[idx
])
1033 add_cprop_bb (cfg
, table
->table
[idx
], bblist
);
1036 for (i
= 0; i
< table
->table_size
; i
++)
1037 if (table
->table
[i
])
1038 add_cprop_bb (cfg
, table
->table
[i
], bblist
);
1042 if (ins
->opcode
== OP_SWITCH
) {
1044 MonoJumpInfoBBTable
*table
= (MonoJumpInfoBBTable
*)ins
->inst_p0
;
1046 for (i
= 0; i
< table
->table_size
; i
++)
1047 if (table
->table
[i
])
1048 add_cprop_bb (cfg
, table
->table
[i
], bblist
);
1051 /* Handle COMPARE+BRCOND pairs */
1052 if (ins
->next
&& MONO_IS_COND_BRANCH_OP (ins
->next
)) {
1054 g_assert (c0
->opcode
== OP_ICONST
);
1057 ins
->next
->flags
|= MONO_INST_CFOLD_TAKEN
;
1059 ins
->next
->flags
|= MONO_INST_CFOLD_NOT_TAKEN
;
1062 ins
->next
->flags
&= ~(MONO_INST_CFOLD_TAKEN
| MONO_INST_CFOLD_NOT_TAKEN
);
1065 visit_inst (cfg
, bb
, ins
->next
, cvars
, bblist
, carray
);
1067 } else if (ins
->opcode
== OP_BR
) {
1068 add_cprop_bb (cfg
, ins
->inst_target_bb
, bblist
);
1069 } else if (MONO_IS_COND_BRANCH_OP (ins
)) {
1070 if (ins
->flags
& MONO_INST_CFOLD_TAKEN
) {
1071 add_cprop_bb (cfg
, ins
->inst_true_bb
, bblist
);
1072 } else if (ins
->flags
& MONO_INST_CFOLD_NOT_TAKEN
) {
1073 if (ins
->inst_false_bb
)
1074 add_cprop_bb (cfg
, ins
->inst_false_bb
, bblist
);
1076 add_cprop_bb (cfg
, ins
->inst_true_bb
, bblist
);
1077 if (ins
->inst_false_bb
)
1078 add_cprop_bb (cfg
, ins
->inst_false_bb
, bblist
);
1086 * Replace INS with its constant value, if it exists
1089 fold_ins (MonoCompile
*cfg
, MonoBasicBlock
*bb
, MonoInst
*ins
, MonoInst
**carray
)
1091 const char *spec
= INS_INFO (ins
->opcode
);
1093 int num_sregs
= mono_inst_get_num_src_registers (ins
);
1095 if ((ins
->opcode
!= OP_NOP
) && (ins
->dreg
!= -1) && !MONO_IS_STORE_MEMBASE (ins
)) {
1096 if (carray
[ins
->dreg
] && (spec
[MONO_INST_DEST
] == 'i') && (ins
->dreg
>= MONO_MAX_IREGS
)) {
1097 /* Perform constant folding */
1099 g_assert (carray
[ins
->dreg
]->opcode
== OP_ICONST
);
1100 ins
->opcode
= OP_ICONST
;
1101 ins
->inst_c0
= carray
[ins
->dreg
]->inst_c0
;
1102 MONO_INST_NULLIFY_SREGS (ins
);
1103 } else if (num_sregs
== 2 && carray
[ins
->sreg2
]) {
1104 /* Perform op->op_imm conversion */
1105 opcode2
= mono_op_to_op_imm (ins
->opcode
);
1106 if (opcode2
!= -1) {
1107 ins
->opcode
= opcode2
;
1108 ins
->inst_imm
= carray
[ins
->sreg2
]->inst_c0
;
1111 if ((opcode2
== OP_VOIDCALL
) || (opcode2
== OP_CALL
) || (opcode2
== OP_LCALL
) || (opcode2
== OP_FCALL
))
1112 ((MonoCallInst
*)ins
)->fptr
= (gpointer
)ins
->inst_imm
;
1115 /* FIXME: Handle 3 op insns */
1118 if (MONO_IS_JUMP_TABLE (ins
)) {
1120 MonoJumpInfoBBTable
*table
= (MonoJumpInfoBBTable
*)MONO_JUMP_TABLE_FROM_INS (ins
);
1122 if (!ins
->next
|| ins
->next
->opcode
!= OP_PADD
) {
1123 /* The PADD was optimized away */
1124 /* FIXME: handle this as well */
1128 g_assert (ins
->next
->opcode
== OP_PADD
);
1129 g_assert (ins
->next
->sreg1
== ins
->dreg
);
1130 g_assert (ins
->next
->next
->opcode
== OP_LOAD_MEMBASE
);
1132 if (carray
[ins
->next
->sreg2
]) {
1133 /* Convert to a simple branch */
1134 #if SIZEOF_REGISTER == 8
1135 int idx
= carray
[ins
->next
->sreg2
]->inst_c0
>> 3;
1137 int idx
= carray
[ins
->next
->sreg2
]->inst_c0
>> 2;
1140 if (!((idx
>= 0) && (idx
< table
->table_size
))) {
1141 /* Out of range, eliminate the whole switch */
1142 for (i
= 0; i
< table
->table_size
; ++i
) {
1143 remove_bb_from_phis (cfg
, bb
, table
->table
[i
]);
1144 mono_unlink_bblock (cfg
, bb
, table
->table
[i
]);
1148 NULLIFY_INS (ins
->next
);
1149 NULLIFY_INS (ins
->next
->next
);
1150 if (ins
->next
->next
->next
)
1151 NULLIFY_INS (ins
->next
->next
->next
);
1156 if (!ins
->next
->next
->next
|| ins
->next
->next
->next
->opcode
!= OP_BR_REG
) {
1157 /* A one-way switch which got optimized away */
1158 if (G_UNLIKELY (cfg
->verbose_level
> 1)) {
1159 printf ("\tNo cfold on ");
1160 mono_print_ins (ins
);
1165 if (G_UNLIKELY (cfg
->verbose_level
> 1)) {
1166 printf ("\tcfold on ");
1167 mono_print_ins (ins
);
1170 /* Unlink target bblocks */
1171 for (i
= 0; i
< table
->table_size
; ++i
) {
1172 if (table
->table
[i
] != table
->table
[idx
]) {
1173 remove_bb_from_phis (cfg
, bb
, table
->table
[i
]);
1174 mono_unlink_bblock (cfg
, bb
, table
->table
[i
]);
1178 /* Change the OP_BR_REG to a simple branch */
1179 ins
->next
->next
->next
->opcode
= OP_BR
;
1180 ins
->next
->next
->next
->inst_target_bb
= table
->table
[idx
];
1181 ins
->next
->next
->next
->sreg1
= -1;
1183 /* Nullify the other instructions */
1185 NULLIFY_INS (ins
->next
);
1186 NULLIFY_INS (ins
->next
->next
);
1190 else if (MONO_IS_COND_BRANCH_OP (ins
)) {
1191 if (ins
->flags
& MONO_INST_CFOLD_TAKEN
) {
1192 remove_bb_from_phis (cfg
, bb
, ins
->inst_false_bb
);
1193 mono_unlink_bblock (cfg
, bb
, ins
->inst_false_bb
);
1194 ins
->opcode
= OP_BR
;
1195 ins
->inst_target_bb
= ins
->inst_true_bb
;
1196 } else if (ins
->flags
& MONO_INST_CFOLD_NOT_TAKEN
) {
1197 remove_bb_from_phis (cfg
, bb
, ins
->inst_true_bb
);
1198 mono_unlink_bblock (cfg
, bb
, ins
->inst_true_bb
);
1199 ins
->opcode
= OP_BR
;
1200 ins
->inst_target_bb
= ins
->inst_false_bb
;
1206 mono_ssa_cprop (MonoCompile
*cfg
)
1210 GList
*bblock_list
, *cvars
;
1213 //printf ("SIMPLE OPTS BB%d %s\n", bb->block_num, mono_method_full_name (cfg->method, TRUE));
1215 carray
= g_new0 (MonoInst
*, cfg
->next_vreg
);
1217 if (!(cfg
->comp_done
& MONO_COMP_SSA_DEF_USE
))
1218 mono_ssa_create_def_use (cfg
);
1220 bblock_list
= g_list_prepend (NULL
, cfg
->bb_entry
);
1221 cfg
->bb_entry
->flags
|= BB_REACHABLE
;
1223 memset (carray
, 0, sizeof (MonoInst
*) * cfg
->num_varinfo
);
1225 for (i
= 0; i
< cfg
->num_varinfo
; i
++) {
1226 MonoMethodVar
*info
= MONO_VARINFO (cfg
, i
);
1231 for (bb
= cfg
->bb_entry
->next_bb
; bb
; bb
= bb
->next_bb
) {
1233 * FIXME: This should be bb->flags & BB_FLAG_EXCEPTION_HANDLER, but
1234 * that would still allow unreachable try's to be removed.
1237 add_cprop_bb (cfg
, bb
, &bblock_list
);
1242 while (bblock_list
) {
1245 bb
= (MonoBasicBlock
*)bblock_list
->data
;
1247 bblock_list
= g_list_delete_link (bblock_list
, bblock_list
);
1249 g_assert (bb
->flags
& BB_REACHABLE
);
1252 * Some bblocks are linked to 2 others even through they fall through to the
1255 if (!(bb
->last_ins
&& MONO_IS_BRANCH_OP (bb
->last_ins
))) {
1256 for (i
= 0; i
< bb
->out_count
; ++i
)
1257 add_cprop_bb (cfg
, bb
->out_bb
[i
], &bblock_list
);
1260 if (cfg
->verbose_level
> 1)
1261 printf ("\nSSA CONSPROP BB%d:\n", bb
->block_num
);
1263 for (inst
= bb
->code
; inst
; inst
= inst
->next
) {
1264 visit_inst (cfg
, bb
, inst
, &cvars
, &bblock_list
, carray
);
1268 MonoMethodVar
*info
= (MonoMethodVar
*)cvars
->data
;
1269 cvars
= g_list_delete_link (cvars
, cvars
);
1271 for (tmp
= info
->uses
; tmp
; tmp
= tmp
->next
) {
1272 MonoVarUsageInfo
*ui
= (MonoVarUsageInfo
*)tmp
->data
;
1273 if (!(ui
->bb
->flags
& BB_REACHABLE
))
1275 visit_inst (cfg
, ui
->bb
, ui
->inst
, &cvars
, &bblock_list
, carray
);
1280 for (bb
= cfg
->bb_entry
->next_bb
; bb
; bb
= bb
->next_bb
) {
1282 for (inst
= bb
->code
; inst
; inst
= inst
->next
) {
1283 fold_ins (cfg
, bb
, inst
, carray
);
1289 cfg
->comp_done
|= MONO_COMP_REACHABILITY
;
1291 /* fixme: we should update usage infos during cprop, instead of computing it again */
1292 cfg
->comp_done
&= ~MONO_COMP_SSA_DEF_USE
;
1293 for (i
= 0; i
< cfg
->num_varinfo
; i
++) {
1294 MonoMethodVar
*info
= MONO_VARINFO (cfg
, i
);
1301 add_to_dce_worklist (MonoCompile
*cfg
, MonoMethodVar
*var
, MonoMethodVar
*use
, GList
**wl
)
1305 *wl
= g_list_prepend_mempool (cfg
->mempool
, *wl
, use
);
1307 for (tmp
= use
->uses
; tmp
; tmp
= tmp
->next
) {
1308 MonoVarUsageInfo
*ui
= (MonoVarUsageInfo
*)tmp
->data
;
1309 if (ui
->inst
== var
->def
) {
1310 /* from the mempool */
1311 use
->uses
= g_list_remove_link (use
->uses
, tmp
);
1318 mono_ssa_deadce (MonoCompile
*cfg
)
1323 g_assert (cfg
->comp_done
& MONO_COMP_SSA
);
1325 //printf ("DEADCE %s\n", mono_method_full_name (cfg->method, TRUE));
1327 if (!(cfg
->comp_done
& MONO_COMP_SSA_DEF_USE
))
1328 mono_ssa_create_def_use (cfg
);
1330 mono_ssa_copyprop (cfg
);
1333 for (i
= 0; i
< cfg
->num_varinfo
; i
++) {
1334 MonoMethodVar
*info
= MONO_VARINFO (cfg
, i
);
1335 work_list
= g_list_prepend_mempool (cfg
->mempool
, work_list
, info
);
1339 MonoMethodVar
*info
= (MonoMethodVar
*)work_list
->data
;
1340 work_list
= g_list_remove_link (work_list
, work_list
);
1343 * The second part of the condition happens often when PHI nodes have their dreg
1344 * as one of their arguments due to the fact that we use the original vars.
1346 if (info
->def
&& (!info
->uses
|| ((info
->uses
->next
== NULL
) && (((MonoVarUsageInfo
*)info
->uses
->data
)->inst
== info
->def
)))) {
1347 MonoInst
*def
= info
->def
;
1349 /* Eliminating FMOVE could screw up the fp stack */
1350 if (MONO_IS_MOVE (def
) && (!MONO_ARCH_USE_FPSTACK
|| (def
->opcode
!= OP_FMOVE
))) {
1351 MonoInst
*src_var
= get_vreg_to_inst (cfg
, def
->sreg1
);
1352 if (src_var
&& !(src_var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
)))
1353 add_to_dce_worklist (cfg
, info
, MONO_VARINFO (cfg
, src_var
->inst_c0
), &work_list
);
1356 } else if ((def
->opcode
== OP_ICONST
) || (def
->opcode
== OP_I8CONST
) || MONO_IS_ZERO (def
)) {
1359 } else if (MONO_IS_PHI (def
)) {
1361 for (j
= def
->inst_phi_args
[0]; j
> 0; j
--) {
1362 MonoMethodVar
*u
= MONO_VARINFO (cfg
, get_vreg_to_inst (cfg
, def
->inst_phi_args
[j
])->inst_c0
);
1363 add_to_dce_worklist (cfg
, info
, u
, &work_list
);
1368 else if (def
->opcode
== OP_NOP
) {
1371 //mono_print_ins (def);
1379 mono_ssa_strength_reduction (MonoCompile
*cfg
)
1384 g_assert (cfg
->comp_done
& MONO_COMP_SSA
);
1385 g_assert (cfg
->comp_done
& MONO_COMP_LOOPS
);
1386 g_assert (cfg
->comp_done
& MONO_COMP_SSA_DEF_USE
);
1388 for (bb
= cfg
->bb_entry
->next_bb
; bb
; bb
= bb
->next_bb
) {
1389 GList
*lp
= bb
->loop_blocks
;
1392 MonoBasicBlock
*h
= (MonoBasicBlock
*)lp
->data
;
1394 /* we only consider loops with 2 in bblocks */
1395 if (!h
->in_count
== 2)
1398 for (i
= 0; i
< cfg
->num_varinfo
; i
++) {
1399 MonoMethodVar
*info
= MONO_VARINFO (cfg
, i
);
1401 if (info
->def
&& info
->def
->ssa_op
== MONO_SSA_STORE
&&
1402 info
->def
->inst_i0
->opcode
== OP_LOCAL
&& g_list_find (lp
, info
->def_bb
)) {
1403 MonoInst
*v
= info
->def
->inst_i1
;
1406 printf ("FOUND %d in %s\n", info
->idx
, mono_method_full_name (cfg
->method
, TRUE
));
1415 mono_ssa_loop_invariant_code_motion (MonoCompile
*cfg
)
1417 MonoBasicBlock
*bb
, *h
, *idom
;
1418 MonoInst
*ins
, *n
, *tins
;
1421 g_assert (cfg
->comp_done
& MONO_COMP_SSA
);
1422 if (!(cfg
->comp_done
& MONO_COMP_LOOPS
) || !(cfg
->comp_done
& MONO_COMP_SSA_DEF_USE
))
1425 for (bb
= cfg
->bb_entry
->next_bb
; bb
; bb
= bb
->next_bb
) {
1426 GList
*lp
= bb
->loop_blocks
;
1430 h
= (MonoBasicBlock
*)lp
->data
;
1433 MONO_BB_FOR_EACH_INS_SAFE (bb
, n
, ins
) {
1435 * Try to move instructions out of loop headers into the preceeding bblock.
1437 if (ins
->opcode
== OP_LDLEN
|| ins
->opcode
== OP_STRLEN
|| ins
->opcode
== OP_CHECK_THIS
|| ins
->opcode
== OP_AOTCONST
|| ins
->opcode
== OP_GENERIC_CLASS_INIT
) {
1443 * h->nesting is needed to work around:
1444 * http://llvm.org/bugs/show_bug.cgi?id=17868
1446 if (!(idom
&& idom
->last_ins
&& idom
->last_ins
->opcode
== OP_BR
&& idom
->last_ins
->inst_target_bb
== h
&& h
->nesting
== 1)) {
1451 * Make sure there are no instructions with side effects before ins.
1454 MONO_BB_FOR_EACH_INS (bb
, tins
) {
1457 if (!MONO_INS_HAS_NO_SIDE_EFFECT (tins
)) {
1464 printf ("%s\n", mono_method_full_name (cfg->method, TRUE));
1465 mono_print_ins (tins);
1470 /* Make sure we don't move the instruction before the def of its sreg */
1471 if (ins
->opcode
== OP_LDLEN
|| ins
->opcode
== OP_STRLEN
|| ins
->opcode
== OP_CHECK_THIS
)
1476 MonoInst
*tins
, *var
;
1479 for (tins
= ins
->prev
; tins
; tins
= tins
->prev
) {
1480 const char *spec
= INS_INFO (tins
->opcode
);
1482 if (tins
->opcode
== OP_MOVE
&& tins
->dreg
== sreg
) {
1484 } if (spec
[MONO_INST_DEST
] != ' ' && tins
->dreg
== sreg
) {
1491 var
= get_vreg_to_inst (cfg
, sreg
);
1492 if (var
&& (var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
)))
1497 if (cfg
->verbose_level
> 1) {
1498 printf ("licm in BB%d on ", bb
->block_num
);
1499 mono_print_ins (ins
);
1501 //{ static int count = 0; count ++; printf ("%d\n", count); }
1502 MONO_REMOVE_INS (bb
, ins
);
1503 mono_bblock_insert_before_ins (idom
, idom
->last_ins
, ins
);
1504 if (ins
->opcode
== OP_LDLEN
|| ins
->opcode
== OP_STRLEN
)
1505 idom
->has_array_access
= TRUE
;
1510 cfg
->comp_done
&= ~MONO_COMP_SSA_DEF_USE
;
1511 for (i
= 0; i
< cfg
->num_varinfo
; i
++) {
1512 MonoMethodVar
*info
= MONO_VARINFO (cfg
, i
);
1518 #endif /* DISABLE_JIT */