3 * Static single assign form support for the JIT compiler.
6 * Dietmar Maurer (dietmar@ximian.com)
8 * (C) 2003 Ximian, Inc.
9 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
14 #include <mono/metadata/debug-helpers.h>
15 #include <mono/metadata/mempool.h>
16 #include <mono/metadata/mempool-internals.h>
17 #include <mono/utils/mono-compiler.h>
22 #include "mini-runtime.h"
27 #define CREATE_PRUNED_SSA
31 #define NEW_PHI(cfg,dest,val) do { \
32 MONO_INST_NEW ((cfg), (dest), OP_PHI); \
33 (dest)->inst_c0 = (val); \
42 unlink_target (MonoBasicBlock
*bb
, MonoBasicBlock
*target
)
46 for (i
= 0; i
< bb
->out_count
; i
++) {
47 if (bb
->out_bb
[i
] == target
) {
48 bb
->out_bb
[i
] = bb
->out_bb
[--bb
->out_count
];
52 for (i
= 0; i
< target
->in_count
; i
++) {
53 if (target
->in_bb
[i
] == bb
) {
54 target
->in_bb
[i
] = target
->in_bb
[--target
->in_count
];
62 unlink_unused_bblocks (MonoCompile
*cfg
)
67 g_assert (cfg
->comp_done
& MONO_COMP_REACHABILITY
);
69 if (G_UNLIKELY (cfg
->verbose_level
> 1))
70 printf ("\nUNLINK UNUSED BBLOCKS:\n");
72 for (bb
= cfg
->bb_entry
; bb
&& bb
->next_bb
;) {
73 if (!(bb
->next_bb
->flags
& BB_REACHABLE
)) {
74 bb
->next_bb
= bb
->next_bb
->next_bb
;
79 for (i
= 1; i
< cfg
->num_bblocks
; i
++) {
80 bb
= cfg
->bblocks
[i
];
82 if (!(bb
->flags
& BB_REACHABLE
)) {
83 for (j
= 0; j
< bb
->in_count
; j
++) {
84 unlink_target (bb
->in_bb
[j
], bb
);
86 for (j
= 0; j
< bb
->out_count
; j
++) {
87 unlink_target (bb
, bb
->out_bb
[j
]);
89 if (G_UNLIKELY (cfg
->verbose_level
> 1))
90 printf ("\tUnlinked BB%d\n", bb
->block_num
);
97 * remove_bb_from_phis:
99 * Remove BB from the PHI statements in TARGET.
102 remove_bb_from_phis (MonoCompile
*cfg
, MonoBasicBlock
*bb
, MonoBasicBlock
*target
)
107 for (i
= 0; i
< target
->in_count
; i
++) {
108 if (target
->in_bb
[i
] == bb
) {
112 g_assert (i
< target
->in_count
);
114 for (ins
= target
->code
; ins
; ins
= ins
->next
) {
115 if (MONO_IS_PHI (ins
)) {
116 for (j
= i
; j
< ins
->inst_phi_args
[0] - 1; ++j
)
117 ins
->inst_phi_args
[j
+ 1] = ins
->inst_phi_args
[j
+ 2];
118 ins
->inst_phi_args
[0] --;
126 op_phi_to_move (int opcode
)
138 g_assert_not_reached ();
145 record_use (MonoCompile
*cfg
, MonoInst
*var
, MonoBasicBlock
*bb
, MonoInst
*ins
)
148 MonoVarUsageInfo
*ui
= (MonoVarUsageInfo
*)mono_mempool_alloc (cfg
->mempool
, sizeof (MonoVarUsageInfo
));
150 info
= MONO_VARINFO (cfg
, var
->inst_c0
);
154 info
->uses
= g_list_prepend_mempool (cfg
->mempool
, info
->uses
, ui
);
163 * mono_ssa_rename_vars:
164 * Implement renaming of SSA variables. Also compute def-use information in parallel.
165 * \p stack_history points to an area of memory which can be used for storing changes
166 * made to the stack, so they can be reverted later.
169 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
)
171 MonoInst
*ins
, *new_var
;
174 int stack_history_len
= 0;
176 if (cfg
->verbose_level
>= 4)
177 printf ("\nRENAME VARS BLOCK %d:\n", bb
->block_num
);
179 /* First pass: Create new vars */
180 for (ins
= bb
->code
; ins
; ins
= ins
->next
) {
181 const char *spec
= INS_INFO (ins
->opcode
);
183 int sregs
[MONO_MAX_SRC_REGS
];
186 printf ("\tProcessing "); mono_print_ins (ins
);
188 if (ins
->opcode
== OP_NOP
)
192 num_sregs
= mono_inst_get_src_registers (ins
, sregs
);
193 for (i
= 0; i
< num_sregs
; ++i
) {
194 if (spec
[MONO_INST_SRC1
+ i
] != ' ') {
195 MonoInst
*var
= get_vreg_to_inst (cfg
, sregs
[i
]);
196 if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
))) {
197 int idx
= var
->inst_c0
;
199 if (var
->opcode
!= OP_ARG
)
200 g_assert (stack
[idx
]);
201 sregs
[i
] = stack
[idx
]->dreg
;
202 record_use (cfg
, stack
[idx
], bb
, ins
);
205 record_use (cfg
, var
, bb
, ins
);
207 else if (G_UNLIKELY (!var
&& lvreg_stack
[sregs
[i
]]))
208 sregs
[i
] = lvreg_stack
[sregs
[i
]];
211 mono_inst_set_src_registers (ins
, sregs
);
213 if (MONO_IS_STORE_MEMBASE (ins
)) {
214 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->dreg
);
215 if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
))) {
216 int idx
= var
->inst_c0
;
218 if (var
->opcode
!= OP_ARG
)
219 g_assert (stack
[idx
]);
220 ins
->dreg
= stack
[idx
]->dreg
;
221 record_use (cfg
, stack
[idx
], bb
, ins
);
224 record_use (cfg
, var
, bb
, ins
);
226 else if (G_UNLIKELY (!var
&& lvreg_stack
[ins
->dreg
]))
227 ins
->dreg
= lvreg_stack
[ins
->dreg
];
231 if ((spec
[MONO_INST_DEST
] != ' ') && !MONO_IS_STORE_MEMBASE (ins
)) {
232 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->dreg
);
235 if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
))) {
237 g_assert (idx
< max_vars
);
239 if (var
->opcode
== OP_ARG
)
240 originals_used
[idx
] = TRUE
;
242 if (stack_history_len
+ 128 > stack_history_size
) {
243 stack_history_size
+= 1024;
244 RenameInfo
*new_history
= mono_mempool_alloc (cfg
->mempool
, sizeof (RenameInfo
) * stack_history_size
);
245 memcpy (new_history
, stack_history
, stack_history_len
* sizeof (RenameInfo
));
246 stack_history
= new_history
;
249 stack_history
[stack_history_len
].var
= stack
[idx
];
250 stack_history
[stack_history_len
].idx
= idx
;
251 stack_history_len
++;
253 if (originals_used
[idx
]) {
254 new_var
= mono_compile_create_var (cfg
, var
->inst_vtype
, OP_LOCAL
);
255 new_var
->flags
= var
->flags
;
256 MONO_VARINFO (cfg
, new_var
->inst_c0
)->reg
= idx
;
258 if (cfg
->verbose_level
>= 4)
259 printf (" R%d -> R%d\n", var
->dreg
, new_var
->dreg
);
261 stack
[idx
] = new_var
;
263 ins
->dreg
= new_var
->dreg
;
268 originals_used
[idx
] = TRUE
;
271 info
= MONO_VARINFO (cfg
, var
->inst_c0
);
275 else if (G_UNLIKELY (!var
&& lvreg_defined
[ins
->dreg
] && (ins
->dreg
>= MONO_MAX_IREGS
))) {
276 /* Perform renaming for local vregs */
277 lvreg_stack
[ins
->dreg
] = vreg_is_ref (cfg
, ins
->dreg
) ? mono_alloc_ireg_ref (cfg
) : mono_alloc_preg (cfg
);
278 ins
->dreg
= lvreg_stack
[ins
->dreg
];
281 lvreg_defined
[ins
->dreg
] = TRUE
;
285 printf ("\tAfter processing "); mono_print_ins (ins
);
290 /* Rename PHI arguments in succeeding bblocks */
291 for (i
= 0; i
< bb
->out_count
; i
++) {
292 MonoBasicBlock
*n
= bb
->out_bb
[i
];
294 for (j
= 0; j
< n
->in_count
; j
++)
295 if (n
->in_bb
[j
] == bb
)
298 for (ins
= n
->code
; ins
; ins
= ins
->next
) {
299 if (MONO_IS_PHI (ins
)) {
302 new_var
= stack
[idx
];
304 new_var
= cfg
->varinfo
[idx
];
306 printf ("FOUND PHI %d (%d, %d)\n", idx
, j
, new_var
->inst_c0
);
308 ins
->inst_phi_args
[j
+ 1] = new_var
->dreg
;
309 record_use (cfg
, new_var
, n
, ins
);
310 if (G_UNLIKELY (cfg
->verbose_level
>= 4))
311 printf ("\tAdd PHI R%d <- R%d to BB%d\n", ins
->dreg
, new_var
->dreg
, n
->block_num
);
314 /* The phi nodes are at the beginning of the bblock */
320 for (tmp
= bb
->dominated
; tmp
; tmp
= tmp
->next
) {
321 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
);
326 for (i
= stack_history_len
- 1; i
>= 0; i
--) {
327 stack
[stack_history
[i
].idx
] = stack_history
[i
].var
;
330 cfg
->comp_done
|= MONO_COMP_SSA_DEF_USE
;
334 mono_ssa_compute (MonoCompile
*cfg
)
336 int i
, j
, idx
, bitsize
;
338 MonoMethodVar
*vinfo
= g_new0 (MonoMethodVar
, cfg
->num_varinfo
);
339 MonoInst
*ins
, **stack
;
340 guint8
*buf
, *buf_start
;
341 RenameInfo
*stack_history
;
342 int stack_history_size
;
344 guint32
*lvreg_stack
;
345 gboolean
*lvreg_defined
;
347 g_assert (!(cfg
->comp_done
& MONO_COMP_SSA
));
349 g_assert (!cfg
->disable_ssa
);
351 if (cfg
->verbose_level
>= 4)
352 printf ("\nCOMPUTE SSA %d (R%d-)\n\n", cfg
->num_varinfo
, cfg
->next_vreg
);
354 #ifdef CREATE_PRUNED_SSA
355 /* we need liveness for pruned SSA */
356 if (!(cfg
->comp_done
& MONO_COMP_LIVENESS
))
357 mono_analyze_liveness (cfg
);
360 mono_compile_dominator_info (cfg
, MONO_COMP_DOM
| MONO_COMP_IDOM
| MONO_COMP_DFRONTIER
);
362 bitsize
= mono_bitset_alloc_size (cfg
->num_bblocks
, 0);
363 buf
= buf_start
= (guint8
*)g_malloc0 (mono_bitset_alloc_size (cfg
->num_bblocks
, 0) * cfg
->num_varinfo
);
365 for (i
= 0; i
< cfg
->num_varinfo
; ++i
) {
366 vinfo
[i
].def_in
= mono_bitset_mem_new (buf
, cfg
->num_bblocks
, 0);
369 /* implicit reference at start */
370 if (cfg
->varinfo
[i
]->opcode
== OP_ARG
)
371 mono_bitset_set_fast (vinfo
[i
].def_in
, 0);
374 for (i
= 0; i
< cfg
->num_bblocks
; ++i
) {
375 MONO_BB_FOR_EACH_INS (cfg
->bblocks
[i
], ins
) {
376 if (ins
->opcode
== OP_NOP
)
379 if (!MONO_IS_STORE_MEMBASE (ins
) && get_vreg_to_inst (cfg
, ins
->dreg
)) {
380 mono_bitset_set_fast (vinfo
[get_vreg_to_inst (cfg
, ins
->dreg
)->inst_c0
].def_in
, i
);
385 /* insert phi functions */
386 for (i
= 0; i
< cfg
->num_varinfo
; ++i
) {
387 MonoInst
*var
= cfg
->varinfo
[i
];
389 #if SIZEOF_REGISTER == 4
390 if (var
->type
== STACK_I8
&& !COMPILE_LLVM (cfg
))
393 if (var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
))
396 /* Most variables have only one definition */
397 if (mono_bitset_count (vinfo
[i
].def_in
) <= 1)
400 set
= mono_compile_iterated_dfrontier (cfg
, vinfo
[i
].def_in
);
402 if (cfg
->verbose_level
>= 4) {
403 if (mono_bitset_count (set
) > 0) {
404 printf ("\tR%d needs PHI functions in ", var
->dreg
);
405 mono_blockset_print (cfg
, set
, "", -1);
409 mono_bitset_foreach_bit (set
, idx
, cfg
->num_bblocks
) {
410 MonoBasicBlock
*bb
= cfg
->bblocks
[idx
];
412 /* fixme: create pruned SSA? we would need liveness information for that */
414 if (bb
== cfg
->bb_exit
&& !COMPILE_LLVM (cfg
))
417 if ((cfg
->comp_done
& MONO_COMP_LIVENESS
) && !mono_bitset_test_fast (bb
->live_in_set
, i
)) {
418 //printf ("%d is not live in BB%d %s\n", i, bb->block_num, mono_method_full_name (cfg->method, TRUE));
422 NEW_PHI (cfg
, ins
, i
);
430 ins
->opcode
= OP_PHI
;
433 ins
->opcode
= OP_FPHI
;
436 ins
->opcode
= MONO_CLASS_IS_SIMD (cfg
, var
->klass
) ? OP_XPHI
: OP_VPHI
;
440 if (var
->inst_vtype
->byref
)
441 ins
->klass
= mono_defaults
.int_class
;
443 ins
->klass
= var
->klass
;
445 ins
->inst_phi_args
= (int *)mono_mempool_alloc0 (cfg
->mempool
, sizeof (int) * (cfg
->bblocks
[idx
]->in_count
+ 1));
446 ins
->inst_phi_args
[0] = cfg
->bblocks
[idx
]->in_count
;
449 for (j
= 0; j
< cfg
->bblocks
[idx
]->in_count
; ++j
)
450 ins
->inst_phi_args
[j
+ 1] = -1;
452 ins
->dreg
= cfg
->varinfo
[i
]->dreg
;
454 mono_bblock_insert_before_ins (bb
, bb
->code
, ins
);
457 printf ("ADD PHI BB%d %s\n", cfg
->bblocks
[idx
]->block_num
, mono_method_full_name (cfg
->method
, TRUE
));
468 stack
= (MonoInst
**)alloca (sizeof (MonoInst
*) * cfg
->num_varinfo
);
469 memset (stack
, 0, sizeof (MonoInst
*) * cfg
->num_varinfo
);
471 lvreg_stack
= g_new0 (guint32
, cfg
->next_vreg
);
472 lvreg_defined
= g_new0 (gboolean
, cfg
->next_vreg
);
473 stack_history_size
= 10240;
474 stack_history
= g_new (RenameInfo
, stack_history_size
);
475 originals
= g_new0 (gboolean
, cfg
->num_varinfo
);
476 mono_ssa_rename_vars (cfg
, cfg
->num_varinfo
, cfg
->bb_entry
, originals
, stack
, lvreg_stack
, lvreg_defined
, stack_history
, stack_history_size
);
477 g_free (stack_history
);
479 g_free (lvreg_stack
);
480 g_free (lvreg_defined
);
482 if (cfg
->verbose_level
>= 4)
483 printf ("\nEND COMPUTE SSA.\n\n");
485 cfg
->comp_done
|= MONO_COMP_SSA
;
489 * mono_ssa_remove_gsharedvt:
491 * Same as mono_ssa_remove, but only remove phi nodes for gsharedvt variables.
494 mono_ssa_remove_gsharedvt (MonoCompile
*cfg
)
496 MonoInst
*ins
, *var
, *move
;
500 * When compiling gsharedvt code, we need to get rid of the VPHI instructions,
501 * since they cannot be handled later in the llvm backend.
503 g_assert (cfg
->comp_done
& MONO_COMP_SSA
);
505 for (i
= 0; i
< cfg
->num_bblocks
; ++i
) {
506 MonoBasicBlock
*bb
= cfg
->bblocks
[i
];
508 if (cfg
->verbose_level
>= 4)
509 printf ("\nREMOVE SSA %d:\n", bb
->block_num
);
511 for (ins
= bb
->code
; ins
; ins
= ins
->next
) {
512 if (!(MONO_IS_PHI (ins
) && ins
->opcode
== OP_VPHI
&& mini_is_gsharedvt_variable_type (m_class_get_byval_arg (ins
->klass
))))
515 g_assert (ins
->inst_phi_args
[0] == bb
->in_count
);
516 var
= get_vreg_to_inst (cfg
, ins
->dreg
);
518 /* Check for PHI nodes where all the inputs are the same */
519 first
= ins
->inst_phi_args
[1];
521 for (j
= 1; j
< bb
->in_count
; ++j
)
522 if (first
!= ins
->inst_phi_args
[j
+ 1])
525 if ((bb
->in_count
> 1) && (j
== bb
->in_count
)) {
526 ins
->opcode
= op_phi_to_move (ins
->opcode
);
527 if (ins
->opcode
== OP_VMOVE
)
528 g_assert (ins
->klass
);
531 for (j
= 0; j
< bb
->in_count
; j
++) {
532 MonoBasicBlock
*pred
= bb
->in_bb
[j
];
533 int sreg
= ins
->inst_phi_args
[j
+ 1];
535 if (cfg
->verbose_level
>= 4)
536 printf ("\tADD R%d <- R%d in BB%d\n", var
->dreg
, sreg
, pred
->block_num
);
537 if (var
->dreg
!= sreg
) {
538 MONO_INST_NEW (cfg
, move
, op_phi_to_move (ins
->opcode
));
539 if (move
->opcode
== OP_VMOVE
) {
540 g_assert (ins
->klass
);
541 move
->klass
= ins
->klass
;
543 move
->dreg
= var
->dreg
;
545 mono_add_ins_to_end (pred
, move
);
556 mono_ssa_remove (MonoCompile
*cfg
)
558 MonoInst
*ins
, *var
, *move
;
559 int bbindex
, i
, j
, first
;
561 g_assert (cfg
->comp_done
& MONO_COMP_SSA
);
563 for (i
= 0; i
< cfg
->num_bblocks
; ++i
) {
564 MonoBasicBlock
*bb
= cfg
->bblocks
[i
];
566 if (cfg
->verbose_level
>= 4)
567 printf ("\nREMOVE SSA %d:\n", bb
->block_num
);
569 for (ins
= bb
->code
; ins
; ins
= ins
->next
) {
570 if (MONO_IS_PHI (ins
)) {
571 g_assert (ins
->inst_phi_args
[0] == bb
->in_count
);
572 var
= get_vreg_to_inst (cfg
, ins
->dreg
);
574 /* Check for PHI nodes where all the inputs are the same */
575 first
= ins
->inst_phi_args
[1];
577 for (j
= 1; j
< bb
->in_count
; ++j
)
578 if (first
!= ins
->inst_phi_args
[j
+ 1])
581 if ((bb
->in_count
> 1) && (j
== bb
->in_count
)) {
582 ins
->opcode
= op_phi_to_move (ins
->opcode
);
583 if (ins
->opcode
== OP_VMOVE
)
584 g_assert (ins
->klass
);
587 for (j
= 0; j
< bb
->in_count
; j
++) {
588 MonoBasicBlock
*pred
= bb
->in_bb
[j
];
589 int sreg
= ins
->inst_phi_args
[j
+ 1];
591 if (cfg
->verbose_level
>= 4)
592 printf ("\tADD R%d <- R%d in BB%d\n", var
->dreg
, sreg
, pred
->block_num
);
593 if (var
->dreg
!= sreg
) {
594 MONO_INST_NEW (cfg
, move
, op_phi_to_move (ins
->opcode
));
595 if (move
->opcode
== OP_VMOVE
) {
596 g_assert (ins
->klass
);
597 move
->klass
= ins
->klass
;
599 move
->dreg
= var
->dreg
;
601 mono_add_ins_to_end (pred
, move
);
611 if (cfg
->verbose_level
>= 4) {
612 for (i
= 0; i
< cfg
->num_bblocks
; ++i
) {
613 MonoBasicBlock
*bb
= cfg
->bblocks
[i
];
615 mono_print_bb (bb
, "AFTER REMOVE SSA:");
620 * Removal of SSA form introduces many copies. To avoid this, we tyry to coalesce
621 * the variables if possible. Since the newly introduced SSA variables don't
622 * have overlapping live ranges (because we don't do agressive optimization), we
623 * can coalesce them into the original variable.
626 for (bbindex
= 0; bbindex
< cfg
->num_bblocks
; ++bbindex
) {
627 MonoBasicBlock
*bb
= cfg
->bblocks
[bbindex
];
629 for (ins
= bb
->code
; ins
; ins
= ins
->next
) {
630 const char *spec
= INS_INFO (ins
->opcode
);
632 int sregs
[MONO_MAX_SRC_REGS
];
634 if (ins
->opcode
== OP_NOP
)
637 if (spec
[MONO_INST_DEST
] != ' ') {
638 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->dreg
);
641 MonoMethodVar
*vmv
= MONO_VARINFO (cfg
, var
->inst_c0
);
644 * The third condition avoids coalescing with variables eliminated
647 if ((vmv
->reg
!= -1) && (vmv
->idx
!= vmv
->reg
) && (MONO_VARINFO (cfg
, vmv
->reg
)->reg
!= -1)) {
648 printf ("COALESCE: R%d -> R%d\n", ins
->dreg
, cfg
->varinfo
[vmv
->reg
]->dreg
);
649 ins
->dreg
= cfg
->varinfo
[vmv
->reg
]->dreg
;
654 num_sregs
= mono_inst_get_src_registers (ins
, sregs
);
655 for (i
= 0; i
< num_sregs
; ++i
) {
656 MonoInst
*var
= get_vreg_to_inst (cfg
, sregs
[i
]);
659 MonoMethodVar
*vmv
= MONO_VARINFO (cfg
, var
->inst_c0
);
661 if ((vmv
->reg
!= -1) && (vmv
->idx
!= vmv
->reg
) && (MONO_VARINFO (cfg
, vmv
->reg
)->reg
!= -1)) {
662 printf ("COALESCE: R%d -> R%d\n", sregs
[i
], cfg
->varinfo
[vmv
->reg
]->dreg
);
663 sregs
[i
] = cfg
->varinfo
[vmv
->reg
]->dreg
;
667 mono_inst_set_src_registers (ins
, sregs
);
671 for (i
= 0; i
< cfg
->num_varinfo
; ++i
) {
672 MONO_VARINFO (cfg
, i
)->reg
= -1;
675 if (cfg
->comp_done
& MONO_COMP_REACHABILITY
)
676 unlink_unused_bblocks (cfg
);
678 cfg
->comp_done
&= ~MONO_COMP_LIVENESS
;
680 cfg
->comp_done
&= ~MONO_COMP_SSA
;
684 mono_ssa_create_def_use (MonoCompile
*cfg
)
690 g_assert (!(cfg
->comp_done
& MONO_COMP_SSA_DEF_USE
));
692 for (bb
= cfg
->bb_entry
; bb
; bb
= bb
->next_bb
) {
693 for (ins
= bb
->code
; ins
; ins
= ins
->next
) {
694 const char *spec
= INS_INFO (ins
->opcode
);
697 int sregs
[MONO_MAX_SRC_REGS
];
699 if (ins
->opcode
== OP_NOP
)
703 num_sregs
= mono_inst_get_src_registers (ins
, sregs
);
704 for (i
= 0; i
< num_sregs
; ++i
) {
705 MonoInst
*var
= get_vreg_to_inst (cfg
, sregs
[i
]);
706 if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
)))
707 record_use (cfg
, var
, bb
, ins
);
710 if (MONO_IS_STORE_MEMBASE (ins
)) {
711 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->dreg
);
712 if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
)))
713 record_use (cfg
, var
, bb
, ins
);
716 if (MONO_IS_PHI (ins
)) {
717 for (i
= ins
->inst_phi_args
[0]; i
> 0; i
--) {
718 g_assert (ins
->inst_phi_args
[i
] != -1);
719 record_use (cfg
, get_vreg_to_inst (cfg
, ins
->inst_phi_args
[i
]), bb
, ins
);
724 if ((spec
[MONO_INST_DEST
] != ' ') && !MONO_IS_STORE_MEMBASE (ins
)) {
725 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->dreg
);
727 if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
))) {
728 info
= MONO_VARINFO (cfg
, var
->inst_c0
);
736 cfg
->comp_done
|= MONO_COMP_SSA_DEF_USE
;
740 mono_ssa_copyprop (MonoCompile
*cfg
)
745 g_assert ((cfg
->comp_done
& MONO_COMP_SSA_DEF_USE
));
747 for (index
= 0; index
< cfg
->num_varinfo
; ++index
) {
748 MonoInst
*var
= cfg
->varinfo
[index
];
749 MonoMethodVar
*info
= MONO_VARINFO (cfg
, index
);
751 if (info
->def
&& (MONO_IS_MOVE (info
->def
))) {
752 MonoInst
*var2
= get_vreg_to_inst (cfg
, info
->def
->sreg1
);
754 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
))) {
755 /* Rewrite all uses of var to be uses of var2 */
756 int dreg
= var
->dreg
;
757 int sreg1
= var2
->dreg
;
761 MonoVarUsageInfo
*u
= (MonoVarUsageInfo
*)l
->data
;
762 MonoInst
*ins
= u
->inst
;
763 GList
*next
= l
->next
;
765 int sregs
[MONO_MAX_SRC_REGS
];
767 num_sregs
= mono_inst_get_src_registers (ins
, sregs
);
768 for (i
= 0; i
< num_sregs
; ++i
) {
769 if (sregs
[i
] == dreg
)
773 g_assert (sregs
[i
] == dreg
);
775 mono_inst_set_src_registers (ins
, sregs
);
776 } else if (MONO_IS_STORE_MEMBASE (ins
) && ins
->dreg
== dreg
) {
778 } else if (MONO_IS_PHI (ins
)) {
779 for (i
= ins
->inst_phi_args
[0]; i
> 0; i
--) {
780 int sreg
= ins
->inst_phi_args
[i
];
781 if (sreg
== var
->dreg
)
785 ins
->inst_phi_args
[i
] = sreg1
;
788 g_assert_not_reached ();
790 record_use (cfg
, var2
, u
->bb
, ins
);
800 if (cfg
->verbose_level
>= 4) {
803 for (bb
= cfg
->bb_entry
; bb
; bb
= bb
->next_bb
)
804 mono_print_bb (bb
, "AFTER SSA COPYPROP");
809 evaluate_ins (MonoCompile
*cfg
, MonoInst
*ins
, MonoInst
**res
, MonoInst
**carray
)
811 MonoInst
*args
[MONO_MAX_SRC_REGS
];
812 int rs
[MONO_MAX_SRC_REGS
];
814 gboolean const_args
= TRUE
;
815 const char *spec
= INS_INFO (ins
->opcode
);
817 int sregs
[MONO_MAX_SRC_REGS
];
819 /* Short-circuit this */
820 if (ins
->opcode
== OP_ICONST
) {
825 if (ins
->opcode
== OP_NOP
)
828 num_sregs
= mono_inst_get_src_registers (ins
, sregs
);
829 for (i
= 0; i
< MONO_MAX_SRC_REGS
; ++i
)
831 for (i
= 0; i
< num_sregs
; ++i
) {
832 MonoInst
*var
= get_vreg_to_inst (cfg
, sregs
[i
]);
835 args
[i
] = carray
[sregs
[i
]];
838 else if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
)))
839 rs
[i
] = MONO_VARINFO (cfg
, var
->inst_c0
)->cpstate
;
846 if (num_sregs
> 0 && const_args
) {
847 g_assert (num_sregs
<= 2);
848 if ((spec
[MONO_INST_DEST
] != ' ') && carray
[ins
->dreg
]) {
850 *res
= carray
[ins
->dreg
];
853 c0
= mono_constant_fold_ins (cfg
, ins
, args
[0], args
[1], FALSE
);
855 if (G_UNLIKELY (cfg
->verbose_level
> 1)) {
856 printf ("\t cfold -> ");
863 /* Can't cfold this ins */
869 for (i
= 0; i
< num_sregs
; ++i
) {
877 change_varstate (MonoCompile
*cfg
, GList
**cvars
, MonoMethodVar
*info
, int state
, MonoInst
*c0
, MonoInst
**carray
)
879 if (info
->cpstate
>= state
)
882 info
->cpstate
= state
;
884 if (G_UNLIKELY (cfg
->verbose_level
> 1))
885 printf ("\tState of R%d set to %d\n", cfg
->varinfo
[info
->idx
]->dreg
, info
->cpstate
);
890 carray
[cfg
->varinfo
[info
->idx
]->dreg
] = c0
;
892 if (!g_list_find (*cvars
, info
)) {
893 *cvars
= g_list_prepend (*cvars
, info
);
898 add_cprop_bb (MonoCompile
*cfg
, MonoBasicBlock
*bb
, GList
**bblist
)
900 if (G_UNLIKELY (cfg
->verbose_level
> 1))
901 printf ("\tAdd BB%d to worklist\n", bb
->block_num
);
903 if (!(bb
->flags
& BB_REACHABLE
)) {
904 bb
->flags
|= BB_REACHABLE
;
905 *bblist
= g_list_prepend (*bblist
, bb
);
910 visit_inst (MonoCompile
*cfg
, MonoBasicBlock
*bb
, MonoInst
*ins
, GList
**cvars
, GList
**bblist
, MonoInst
**carray
)
912 const char *spec
= INS_INFO (ins
->opcode
);
914 if (ins
->opcode
== OP_NOP
)
917 if (cfg
->verbose_level
> 1)
918 mono_print_ins (ins
);
920 /* FIXME: Support longs/floats */
921 /* FIXME: Work on vregs as well */
923 if (MONO_IS_PHI (ins
)) {
924 MonoMethodVar
*info
= MONO_VARINFO (cfg
, get_vreg_to_inst (cfg
, ins
->dreg
)->inst_c0
);
928 for (j
= 1; j
<= ins
->inst_phi_args
[0]; j
++) {
929 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->inst_phi_args
[j
]);
930 MonoMethodVar
*mv
= MONO_VARINFO (cfg
, var
->inst_c0
);
931 MonoInst
*src
= mv
->def
;
933 if (mv
->def_bb
&& !(mv
->def_bb
->flags
& BB_REACHABLE
))
936 if (!mv
->def
|| !src
|| mv
->cpstate
== 2) {
937 change_varstate (cfg
, cvars
, info
, 2, NULL
, carray
);
941 if (mv
->cpstate
== 0)
944 g_assert (carray
[var
->dreg
]);
947 c0
= carray
[var
->dreg
];
950 if (c0
->opcode
!= OP_ICONST
) {
951 change_varstate (cfg
, cvars
, info
, 2, NULL
, carray
);
955 if (carray
[var
->dreg
]->inst_c0
!= c0
->inst_c0
) {
956 change_varstate (cfg
, cvars
, info
, 2, NULL
, carray
);
961 if (c0
&& info
->cpstate
< 1) {
962 change_varstate (cfg
, cvars
, info
, 1, c0
, carray
);
964 g_assert (c0
->opcode
== OP_ICONST
);
967 else if (!MONO_IS_STORE_MEMBASE (ins
) && ((spec
[MONO_INST_SRC1
] != ' ') || (spec
[MONO_INST_SRC2
] != ' ') || (spec
[MONO_INST_DEST
] != ' '))) {
971 if (spec
[MONO_INST_DEST
] != ' ')
972 var
= get_vreg_to_inst (cfg
, ins
->dreg
);
977 state
= evaluate_ins (cfg
, ins
, &c0
, carray
);
979 if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
))) {
980 MonoMethodVar
*info
= MONO_VARINFO (cfg
, var
->inst_c0
);
982 if (info
->cpstate
< 2) {
984 change_varstate (cfg
, cvars
, info
, 1, c0
, carray
);
986 change_varstate (cfg
, cvars
, info
, 2, NULL
, carray
);
989 else if (!var
&& (ins
->dreg
!= -1)) {
991 * We don't record def-use information for local vregs since it would be
992 * expensive. Instead, we depend on the fact that all uses of the vreg are in
993 * the same bblock, so they will be examined after the definition.
994 * FIXME: This isn't true if the ins is visited through an SSA edge.
997 carray
[ins
->dreg
] = c0
;
999 if (carray
[ins
->dreg
]) {
1001 * The state of the vreg changed from constant to non-constant
1002 * -> need to rescan the whole bblock.
1004 carray
[ins
->dreg
] = NULL
;
1005 /* FIXME: Speed this up */
1007 if (!g_list_find (*bblist
, bb
))
1008 *bblist
= g_list_prepend (*bblist
, bb
);
1013 if (MONO_IS_JUMP_TABLE (ins
)) {
1015 MonoJumpInfoBBTable
*table
= (MonoJumpInfoBBTable
*)MONO_JUMP_TABLE_FROM_INS (ins
);
1017 if (!ins
->next
|| ins
->next
->opcode
!= OP_PADD
) {
1018 /* The PADD was optimized away */
1019 /* FIXME: handle this as well */
1020 for (i
= 0; i
< table
->table_size
; i
++)
1021 if (table
->table
[i
])
1022 add_cprop_bb (cfg
, table
->table
[i
], bblist
);
1026 g_assert (ins
->next
->opcode
== OP_PADD
);
1027 g_assert (ins
->next
->sreg1
== ins
->dreg
);
1029 if (carray
[ins
->next
->sreg2
]) {
1030 #if SIZEOF_REGISTER == 8
1031 int idx
= carray
[ins
->next
->sreg2
]->inst_c0
>> 3;
1033 int idx
= carray
[ins
->next
->sreg2
]->inst_c0
>> 2;
1035 if ((idx
< 0) || (idx
>= table
->table_size
))
1036 /* Out-of-range, no branch is executed */
1039 if (table
->table
[idx
])
1040 add_cprop_bb (cfg
, table
->table
[idx
], bblist
);
1043 for (i
= 0; i
< table
->table_size
; i
++)
1044 if (table
->table
[i
])
1045 add_cprop_bb (cfg
, table
->table
[i
], bblist
);
1049 if (ins
->opcode
== OP_SWITCH
) {
1051 MonoJumpInfoBBTable
*table
= (MonoJumpInfoBBTable
*)ins
->inst_p0
;
1053 for (i
= 0; i
< table
->table_size
; i
++)
1054 if (table
->table
[i
])
1055 add_cprop_bb (cfg
, table
->table
[i
], bblist
);
1058 /* Handle COMPARE+BRCOND pairs */
1059 if (ins
->next
&& MONO_IS_COND_BRANCH_OP (ins
->next
)) {
1061 g_assert (c0
->opcode
== OP_ICONST
);
1064 ins
->next
->flags
|= MONO_INST_CFOLD_TAKEN
;
1066 ins
->next
->flags
|= MONO_INST_CFOLD_NOT_TAKEN
;
1069 ins
->next
->flags
&= ~(MONO_INST_CFOLD_TAKEN
| MONO_INST_CFOLD_NOT_TAKEN
);
1072 visit_inst (cfg
, bb
, ins
->next
, cvars
, bblist
, carray
);
1074 } else if (ins
->opcode
== OP_BR
) {
1075 add_cprop_bb (cfg
, ins
->inst_target_bb
, bblist
);
1076 } else if (MONO_IS_COND_BRANCH_OP (ins
)) {
1077 if (ins
->flags
& MONO_INST_CFOLD_TAKEN
) {
1078 add_cprop_bb (cfg
, ins
->inst_true_bb
, bblist
);
1079 } else if (ins
->flags
& MONO_INST_CFOLD_NOT_TAKEN
) {
1080 if (ins
->inst_false_bb
)
1081 add_cprop_bb (cfg
, ins
->inst_false_bb
, bblist
);
1083 add_cprop_bb (cfg
, ins
->inst_true_bb
, bblist
);
1084 if (ins
->inst_false_bb
)
1085 add_cprop_bb (cfg
, ins
->inst_false_bb
, bblist
);
1093 * Replace INS with its constant value, if it exists
1096 fold_ins (MonoCompile
*cfg
, MonoBasicBlock
*bb
, MonoInst
*ins
, MonoInst
**carray
)
1098 const char *spec
= INS_INFO (ins
->opcode
);
1100 int num_sregs
= mono_inst_get_num_src_registers (ins
);
1102 if ((ins
->opcode
!= OP_NOP
) && (ins
->dreg
!= -1) && !MONO_IS_STORE_MEMBASE (ins
)) {
1103 if (carray
[ins
->dreg
] && (spec
[MONO_INST_DEST
] == 'i') && (ins
->dreg
>= MONO_MAX_IREGS
)) {
1104 /* Perform constant folding */
1106 g_assert (carray
[ins
->dreg
]->opcode
== OP_ICONST
);
1107 ins
->opcode
= OP_ICONST
;
1108 ins
->inst_c0
= carray
[ins
->dreg
]->inst_c0
;
1109 MONO_INST_NULLIFY_SREGS (ins
);
1110 } else if (num_sregs
== 2 && carray
[ins
->sreg2
]) {
1111 /* Perform op->op_imm conversion */
1112 opcode2
= mono_op_to_op_imm (ins
->opcode
);
1113 if (opcode2
!= -1) {
1114 ins
->opcode
= opcode2
;
1115 ins
->inst_imm
= carray
[ins
->sreg2
]->inst_c0
;
1118 if ((opcode2
== OP_VOIDCALL
) || (opcode2
== OP_CALL
) || (opcode2
== OP_LCALL
) || (opcode2
== OP_FCALL
))
1119 ((MonoCallInst
*)ins
)->fptr
= (gpointer
)ins
->inst_imm
;
1122 /* FIXME: Handle 3 op insns */
1125 if (MONO_IS_JUMP_TABLE (ins
)) {
1127 MonoJumpInfoBBTable
*table
= (MonoJumpInfoBBTable
*)MONO_JUMP_TABLE_FROM_INS (ins
);
1129 if (!ins
->next
|| ins
->next
->opcode
!= OP_PADD
) {
1130 /* The PADD was optimized away */
1131 /* FIXME: handle this as well */
1135 g_assert (ins
->next
->opcode
== OP_PADD
);
1136 g_assert (ins
->next
->sreg1
== ins
->dreg
);
1137 g_assert (ins
->next
->next
->opcode
== OP_LOAD_MEMBASE
);
1139 if (carray
[ins
->next
->sreg2
]) {
1140 /* Convert to a simple branch */
1141 #if SIZEOF_REGISTER == 8
1142 int idx
= carray
[ins
->next
->sreg2
]->inst_c0
>> 3;
1144 int idx
= carray
[ins
->next
->sreg2
]->inst_c0
>> 2;
1147 if (!((idx
>= 0) && (idx
< table
->table_size
))) {
1148 /* Out of range, eliminate the whole switch */
1149 for (i
= 0; i
< table
->table_size
; ++i
) {
1150 remove_bb_from_phis (cfg
, bb
, table
->table
[i
]);
1151 mono_unlink_bblock (cfg
, bb
, table
->table
[i
]);
1155 NULLIFY_INS (ins
->next
);
1156 NULLIFY_INS (ins
->next
->next
);
1157 if (ins
->next
->next
->next
)
1158 NULLIFY_INS (ins
->next
->next
->next
);
1163 if (!ins
->next
->next
->next
|| ins
->next
->next
->next
->opcode
!= OP_BR_REG
) {
1164 /* A one-way switch which got optimized away */
1165 if (G_UNLIKELY (cfg
->verbose_level
> 1)) {
1166 printf ("\tNo cfold on ");
1167 mono_print_ins (ins
);
1172 if (G_UNLIKELY (cfg
->verbose_level
> 1)) {
1173 printf ("\tcfold on ");
1174 mono_print_ins (ins
);
1177 /* Unlink target bblocks */
1178 for (i
= 0; i
< table
->table_size
; ++i
) {
1179 if (table
->table
[i
] != table
->table
[idx
]) {
1180 remove_bb_from_phis (cfg
, bb
, table
->table
[i
]);
1181 mono_unlink_bblock (cfg
, bb
, table
->table
[i
]);
1185 /* Change the OP_BR_REG to a simple branch */
1186 ins
->next
->next
->next
->opcode
= OP_BR
;
1187 ins
->next
->next
->next
->inst_target_bb
= table
->table
[idx
];
1188 ins
->next
->next
->next
->sreg1
= -1;
1190 /* Nullify the other instructions */
1192 NULLIFY_INS (ins
->next
);
1193 NULLIFY_INS (ins
->next
->next
);
1197 else if (MONO_IS_COND_BRANCH_OP (ins
)) {
1198 if (ins
->flags
& MONO_INST_CFOLD_TAKEN
) {
1199 remove_bb_from_phis (cfg
, bb
, ins
->inst_false_bb
);
1200 mono_unlink_bblock (cfg
, bb
, ins
->inst_false_bb
);
1201 ins
->opcode
= OP_BR
;
1202 ins
->inst_target_bb
= ins
->inst_true_bb
;
1203 } else if (ins
->flags
& MONO_INST_CFOLD_NOT_TAKEN
) {
1204 remove_bb_from_phis (cfg
, bb
, ins
->inst_true_bb
);
1205 mono_unlink_bblock (cfg
, bb
, ins
->inst_true_bb
);
1206 ins
->opcode
= OP_BR
;
1207 ins
->inst_target_bb
= ins
->inst_false_bb
;
1213 mono_ssa_cprop (MonoCompile
*cfg
)
1217 GList
*bblock_list
, *cvars
;
1220 //printf ("SIMPLE OPTS BB%d %s\n", bb->block_num, mono_method_full_name (cfg->method, TRUE));
1222 carray
= g_new0 (MonoInst
*, cfg
->next_vreg
);
1224 if (!(cfg
->comp_done
& MONO_COMP_SSA_DEF_USE
))
1225 mono_ssa_create_def_use (cfg
);
1227 bblock_list
= g_list_prepend (NULL
, cfg
->bb_entry
);
1228 cfg
->bb_entry
->flags
|= BB_REACHABLE
;
1230 memset (carray
, 0, sizeof (MonoInst
*) * cfg
->num_varinfo
);
1232 for (i
= 0; i
< cfg
->num_varinfo
; i
++) {
1233 MonoMethodVar
*info
= MONO_VARINFO (cfg
, i
);
1238 for (bb
= cfg
->bb_entry
->next_bb
; bb
; bb
= bb
->next_bb
) {
1240 * FIXME: This should be bb->flags & BB_FLAG_EXCEPTION_HANDLER, but
1241 * that would still allow unreachable try's to be removed.
1244 add_cprop_bb (cfg
, bb
, &bblock_list
);
1249 while (bblock_list
) {
1252 bb
= (MonoBasicBlock
*)bblock_list
->data
;
1254 bblock_list
= g_list_delete_link (bblock_list
, bblock_list
);
1256 g_assert (bb
->flags
& BB_REACHABLE
);
1259 * Some bblocks are linked to 2 others even through they fall through to the
1262 if (!(bb
->last_ins
&& MONO_IS_BRANCH_OP (bb
->last_ins
))) {
1263 for (i
= 0; i
< bb
->out_count
; ++i
)
1264 add_cprop_bb (cfg
, bb
->out_bb
[i
], &bblock_list
);
1267 if (cfg
->verbose_level
> 1)
1268 printf ("\nSSA CONSPROP BB%d:\n", bb
->block_num
);
1270 for (inst
= bb
->code
; inst
; inst
= inst
->next
) {
1271 visit_inst (cfg
, bb
, inst
, &cvars
, &bblock_list
, carray
);
1275 MonoMethodVar
*info
= (MonoMethodVar
*)cvars
->data
;
1276 cvars
= g_list_delete_link (cvars
, cvars
);
1278 for (tmp
= info
->uses
; tmp
; tmp
= tmp
->next
) {
1279 MonoVarUsageInfo
*ui
= (MonoVarUsageInfo
*)tmp
->data
;
1280 if (!(ui
->bb
->flags
& BB_REACHABLE
))
1282 visit_inst (cfg
, ui
->bb
, ui
->inst
, &cvars
, &bblock_list
, carray
);
1287 for (bb
= cfg
->bb_entry
->next_bb
; bb
; bb
= bb
->next_bb
) {
1289 for (inst
= bb
->code
; inst
; inst
= inst
->next
) {
1290 fold_ins (cfg
, bb
, inst
, carray
);
1296 cfg
->comp_done
|= MONO_COMP_REACHABILITY
;
1298 /* fixme: we should update usage infos during cprop, instead of computing it again */
1299 cfg
->comp_done
&= ~MONO_COMP_SSA_DEF_USE
;
1300 for (i
= 0; i
< cfg
->num_varinfo
; i
++) {
1301 MonoMethodVar
*info
= MONO_VARINFO (cfg
, i
);
1308 add_to_dce_worklist (MonoCompile
*cfg
, MonoMethodVar
*var
, MonoMethodVar
*use
, GList
**wl
)
1312 *wl
= g_list_prepend_mempool (cfg
->mempool
, *wl
, use
);
1314 for (tmp
= use
->uses
; tmp
; tmp
= tmp
->next
) {
1315 MonoVarUsageInfo
*ui
= (MonoVarUsageInfo
*)tmp
->data
;
1316 if (ui
->inst
== var
->def
) {
1317 /* from the mempool */
1318 use
->uses
= g_list_remove_link (use
->uses
, tmp
);
1325 mono_ssa_deadce (MonoCompile
*cfg
)
1330 g_assert (cfg
->comp_done
& MONO_COMP_SSA
);
1332 //printf ("DEADCE %s\n", mono_method_full_name (cfg->method, TRUE));
1334 if (!(cfg
->comp_done
& MONO_COMP_SSA_DEF_USE
))
1335 mono_ssa_create_def_use (cfg
);
1337 mono_ssa_copyprop (cfg
);
1340 for (i
= 0; i
< cfg
->num_varinfo
; i
++) {
1341 MonoMethodVar
*info
= MONO_VARINFO (cfg
, i
);
1342 work_list
= g_list_prepend_mempool (cfg
->mempool
, work_list
, info
);
1346 MonoMethodVar
*info
= (MonoMethodVar
*)work_list
->data
;
1347 work_list
= g_list_remove_link (work_list
, work_list
);
1350 * The second part of the condition happens often when PHI nodes have their dreg
1351 * as one of their arguments due to the fact that we use the original vars.
1353 if (info
->def
&& (!info
->uses
|| ((info
->uses
->next
== NULL
) && (((MonoVarUsageInfo
*)info
->uses
->data
)->inst
== info
->def
)))) {
1354 MonoInst
*def
= info
->def
;
1356 /* Eliminating FMOVE could screw up the fp stack */
1357 if (MONO_IS_MOVE (def
) && (!MONO_ARCH_USE_FPSTACK
|| (def
->opcode
!= OP_FMOVE
))) {
1358 MonoInst
*src_var
= get_vreg_to_inst (cfg
, def
->sreg1
);
1359 if (src_var
&& !(src_var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
)))
1360 add_to_dce_worklist (cfg
, info
, MONO_VARINFO (cfg
, src_var
->inst_c0
), &work_list
);
1363 } else if ((def
->opcode
== OP_ICONST
) || (def
->opcode
== OP_I8CONST
) || MONO_IS_ZERO (def
)) {
1366 } else if (MONO_IS_PHI (def
)) {
1368 for (j
= def
->inst_phi_args
[0]; j
> 0; j
--) {
1369 MonoMethodVar
*u
= MONO_VARINFO (cfg
, get_vreg_to_inst (cfg
, def
->inst_phi_args
[j
])->inst_c0
);
1370 add_to_dce_worklist (cfg
, info
, u
, &work_list
);
1375 else if (def
->opcode
== OP_NOP
) {
1378 //mono_print_ins (def);
1386 mono_ssa_strength_reduction (MonoCompile
*cfg
)
1391 g_assert (cfg
->comp_done
& MONO_COMP_SSA
);
1392 g_assert (cfg
->comp_done
& MONO_COMP_LOOPS
);
1393 g_assert (cfg
->comp_done
& MONO_COMP_SSA_DEF_USE
);
1395 for (bb
= cfg
->bb_entry
->next_bb
; bb
; bb
= bb
->next_bb
) {
1396 GList
*lp
= bb
->loop_blocks
;
1399 MonoBasicBlock
*h
= (MonoBasicBlock
*)lp
->data
;
1401 /* we only consider loops with 2 in bblocks */
1402 if (!h
->in_count
== 2)
1405 for (i
= 0; i
< cfg
->num_varinfo
; i
++) {
1406 MonoMethodVar
*info
= MONO_VARINFO (cfg
, i
);
1408 if (info
->def
&& info
->def
->ssa_op
== MONO_SSA_STORE
&&
1409 info
->def
->inst_i0
->opcode
== OP_LOCAL
&& g_list_find (lp
, info
->def_bb
)) {
1410 MonoInst
*v
= info
->def
->inst_i1
;
1413 printf ("FOUND %d in %s\n", info
->idx
, mono_method_full_name (cfg
->method
, TRUE
));
1422 mono_ssa_loop_invariant_code_motion (MonoCompile
*cfg
)
1424 MonoBasicBlock
*bb
, *h
, *idom
;
1425 MonoInst
*ins
, *n
, *tins
;
1428 g_assert (cfg
->comp_done
& MONO_COMP_SSA
);
1429 if (!(cfg
->comp_done
& MONO_COMP_LOOPS
) || !(cfg
->comp_done
& MONO_COMP_SSA_DEF_USE
))
1432 for (bb
= cfg
->bb_entry
->next_bb
; bb
; bb
= bb
->next_bb
) {
1433 GList
*lp
= bb
->loop_blocks
;
1437 h
= (MonoBasicBlock
*)lp
->data
;
1440 MONO_BB_FOR_EACH_INS_SAFE (bb
, n
, ins
) {
1442 * Try to move instructions out of loop headers into the preceeding bblock.
1444 if (ins
->opcode
== OP_LDLEN
|| ins
->opcode
== OP_STRLEN
|| ins
->opcode
== OP_CHECK_THIS
|| ins
->opcode
== OP_AOTCONST
|| ins
->opcode
== OP_GENERIC_CLASS_INIT
) {
1450 * h->nesting is needed to work around:
1451 * http://llvm.org/bugs/show_bug.cgi?id=17868
1453 if (!(idom
&& idom
->last_ins
&& idom
->last_ins
->opcode
== OP_BR
&& idom
->last_ins
->inst_target_bb
== h
&& h
->nesting
== 1)) {
1458 * Make sure there are no instructions with side effects before ins.
1461 MONO_BB_FOR_EACH_INS (bb
, tins
) {
1464 if (!MONO_INS_HAS_NO_SIDE_EFFECT (tins
)) {
1471 printf ("%s\n", mono_method_full_name (cfg->method, TRUE));
1472 mono_print_ins (tins);
1477 /* Make sure we don't move the instruction before the def of its sreg */
1478 if (ins
->opcode
== OP_LDLEN
|| ins
->opcode
== OP_STRLEN
|| ins
->opcode
== OP_CHECK_THIS
)
1483 MonoInst
*tins
, *var
;
1486 for (tins
= ins
->prev
; tins
; tins
= tins
->prev
) {
1487 const char *spec
= INS_INFO (tins
->opcode
);
1489 if (tins
->opcode
== OP_MOVE
&& tins
->dreg
== sreg
) {
1491 } if (spec
[MONO_INST_DEST
] != ' ' && tins
->dreg
== sreg
) {
1498 var
= get_vreg_to_inst (cfg
, sreg
);
1499 if (var
&& (var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
)))
1504 if (cfg
->verbose_level
> 1) {
1505 printf ("licm in BB%d on ", bb
->block_num
);
1506 mono_print_ins (ins
);
1508 //{ static int count = 0; count ++; printf ("%d\n", count); }
1509 MONO_REMOVE_INS (bb
, ins
);
1510 mono_bblock_insert_before_ins (idom
, idom
->last_ins
, ins
);
1511 if (ins
->opcode
== OP_LDLEN
|| ins
->opcode
== OP_STRLEN
)
1512 idom
->needs_decompose
= TRUE
;
1517 cfg
->comp_done
&= ~MONO_COMP_SSA_DEF_USE
;
1518 for (i
= 0; i
< cfg
->num_varinfo
; i
++) {
1519 MonoMethodVar
*info
= MONO_VARINFO (cfg
, i
);
1525 #else /* !DISABLE_JIT */
1527 MONO_EMPTY_SOURCE_FILE (ssa
);
1529 #endif /* !DISABLE_JIT */