2 * ssa.c: Static single assign form support for the JIT compiler.
5 * Dietmar Maurer (dietmar@ximian.com)
7 * (C) 2003 Ximian, Inc.
10 #include <mono/metadata/debug-helpers.h>
11 #include <mono/metadata/mempool.h>
12 #include <mono/metadata/mempool-internals.h>
19 #define USE_ORIGINAL_VARS
20 #define CREATE_PRUNED_SSA
24 #define NEW_PHI(cfg,dest,val) do { \
25 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
26 (dest)->opcode = OP_PHI; \
27 (dest)->inst_c0 = (val); \
28 (dest)->dreg = (dest)->sreg1 = (dest)->sreg2 = -1; \
31 #define NEW_ICONST(cfg,dest,val) do { \
32 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst)); \
33 (dest)->opcode = OP_ICONST; \
34 (dest)->inst_c0 = (val); \
35 (dest)->type = STACK_I4; \
36 (dest)->dreg = (dest)->sreg1 = (dest)->sreg2 = -1; \
45 unlink_target (MonoBasicBlock
*bb
, MonoBasicBlock
*target
)
49 for (i
= 0; i
< bb
->out_count
; i
++) {
50 if (bb
->out_bb
[i
] == target
) {
51 bb
->out_bb
[i
] = bb
->out_bb
[--bb
->out_count
];
55 for (i
= 0; i
< target
->in_count
; i
++) {
56 if (target
->in_bb
[i
] == bb
) {
57 target
->in_bb
[i
] = target
->in_bb
[--target
->in_count
];
65 unlink_unused_bblocks (MonoCompile
*cfg
)
70 g_assert (cfg
->comp_done
& MONO_COMP_REACHABILITY
);
72 if (G_UNLIKELY (cfg
->verbose_level
> 1))
73 printf ("\nUNLINK UNUSED BBLOCKS:\n");
75 for (bb
= cfg
->bb_entry
; bb
&& bb
->next_bb
;) {
76 if (!(bb
->next_bb
->flags
& BB_REACHABLE
)) {
77 bb
->next_bb
= bb
->next_bb
->next_bb
;
82 for (i
= 1; i
< cfg
->num_bblocks
; i
++) {
83 bb
= cfg
->bblocks
[i
];
85 if (!(bb
->flags
& BB_REACHABLE
)) {
86 for (j
= 0; j
< bb
->in_count
; j
++) {
87 unlink_target (bb
->in_bb
[j
], bb
);
89 for (j
= 0; j
< bb
->out_count
; j
++) {
90 unlink_target (bb
, bb
->out_bb
[j
]);
92 if (G_UNLIKELY (cfg
->verbose_level
> 1))
93 printf ("\tUnlinked BB%d\n", bb
->block_num
);
100 * remove_bb_from_phis:
102 * Remove BB from the PHI statements in TARGET.
105 remove_bb_from_phis (MonoCompile
*cfg
, MonoBasicBlock
*bb
, MonoBasicBlock
*target
)
110 for (i
= 0; i
< target
->in_count
; i
++) {
111 if (target
->in_bb
[i
] == bb
) {
115 g_assert (i
< target
->in_count
);
117 for (ins
= target
->code
; ins
; ins
= ins
->next
) {
118 if (MONO_IS_PHI (ins
)) {
119 for (j
= i
; j
< ins
->inst_phi_args
[0] - 1; ++j
)
120 ins
->inst_phi_args
[j
+ 1] = ins
->inst_phi_args
[j
+ 2];
121 ins
->inst_phi_args
[0] --;
129 op_phi_to_move (int opcode
)
141 g_assert_not_reached ();
148 record_use (MonoCompile
*cfg
, MonoInst
*var
, MonoBasicBlock
*bb
, MonoInst
*ins
)
151 MonoVarUsageInfo
*ui
= mono_mempool_alloc (cfg
->mempool
, sizeof (MonoVarUsageInfo
));
153 info
= MONO_VARINFO (cfg
, var
->inst_c0
);
157 info
->uses
= g_list_prepend_mempool (cfg
->mempool
, info
->uses
, ui
);
166 * mono_ssa_rename_vars2:
168 * Implement renaming of SSA variables. Also compute def-use information in parallel.
169 * @stack_history points to an area of memory which can be used for storing changes
170 * made to the stack, so they can be reverted later.
173 mono_ssa_rename_vars2 (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
)
175 MonoInst
*ins
, *new_var
;
178 int stack_history_len
= 0;
180 if (cfg
->verbose_level
>= 4)
181 printf ("\nRENAME VARS BLOCK %d:\n", bb
->block_num
);
183 /* First pass: Create new vars */
184 for (ins
= bb
->code
; ins
; ins
= ins
->next
) {
185 const char *spec
= INS_INFO (ins
->opcode
);
188 printf ("\tProcessing "); mono_print_ins (ins
);
190 if (ins
->opcode
== OP_NOP
)
194 if (spec
[MONO_INST_SRC1
] != ' ') {
195 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->sreg1
);
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 ins
->sreg1
= 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
[ins
->sreg1
]))
208 ins
->sreg1
= lvreg_stack
[ins
->sreg1
];
212 if (spec
[MONO_INST_SRC2
] != ' ') {
213 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->sreg2
);
214 if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
))) {
215 int idx
= var
->inst_c0
;
217 if (var
->opcode
!= OP_ARG
)
218 g_assert (stack
[idx
]);
220 ins
->sreg2
= 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
->sreg2
]))
227 ins
->sreg2
= lvreg_stack
[ins
->sreg2
];
230 if (MONO_IS_STORE_MEMBASE (ins
)) {
231 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->dreg
);
232 if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
))) {
233 int idx
= var
->inst_c0
;
235 if (var
->opcode
!= OP_ARG
)
236 g_assert (stack
[idx
]);
237 ins
->dreg
= stack
[idx
]->dreg
;
238 record_use (cfg
, stack
[idx
], bb
, ins
);
241 record_use (cfg
, var
, bb
, ins
);
243 else if (G_UNLIKELY (!var
&& lvreg_stack
[ins
->dreg
]))
244 ins
->dreg
= lvreg_stack
[ins
->dreg
];
248 if ((spec
[MONO_INST_DEST
] != ' ') && !MONO_IS_STORE_MEMBASE (ins
)) {
249 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->dreg
);
252 if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
))) {
254 g_assert (idx
< max_vars
);
256 if (var
->opcode
== OP_ARG
)
257 originals_used
[idx
] = TRUE
;
260 g_assert (stack_history_len
< stack_history_size
);
261 stack_history
[stack_history_len
].var
= stack
[idx
];
262 stack_history
[stack_history_len
].idx
= idx
;
263 stack_history_len
++;
265 if (originals_used
[idx
]) {
266 new_var
= mono_compile_create_var (cfg
, var
->inst_vtype
, OP_LOCAL
);
267 new_var
->flags
= var
->flags
;
268 MONO_VARINFO (cfg
, new_var
->inst_c0
)->reg
= idx
;
270 if (cfg
->verbose_level
>= 4)
271 printf (" R%d -> R%d\n", var
->dreg
, new_var
->dreg
);
273 stack
[idx
] = new_var
;
275 ins
->dreg
= new_var
->dreg
;
280 originals_used
[idx
] = TRUE
;
283 info
= MONO_VARINFO (cfg
, var
->inst_c0
);
287 else if (G_UNLIKELY (!var
&& lvreg_defined
[ins
->dreg
] && (ins
->dreg
>= MONO_MAX_IREGS
))) {
288 /* Perform renaming for local vregs */
289 lvreg_stack
[ins
->dreg
] = mono_alloc_preg (cfg
);
290 ins
->dreg
= lvreg_stack
[ins
->dreg
];
293 lvreg_defined
[ins
->dreg
] = TRUE
;
297 printf ("\tAfter processing "); mono_print_ins (ins
);
302 /* Rename PHI arguments in succeeding bblocks */
303 for (i
= 0; i
< bb
->out_count
; i
++) {
304 MonoBasicBlock
*n
= bb
->out_bb
[i
];
306 for (j
= 0; j
< n
->in_count
; j
++)
307 if (n
->in_bb
[j
] == bb
)
310 for (ins
= n
->code
; ins
; ins
= ins
->next
) {
311 if (MONO_IS_PHI (ins
)) {
314 new_var
= stack
[idx
];
316 new_var
= cfg
->varinfo
[idx
];
318 printf ("FOUND PHI %d (%d, %d)\n", idx
, j
, new_var
->inst_c0
);
320 ins
->inst_phi_args
[j
+ 1] = new_var
->dreg
;
321 record_use (cfg
, new_var
, n
, ins
);
322 if (G_UNLIKELY (cfg
->verbose_level
>= 4))
323 printf ("\tAdd PHI R%d <- R%d to BB%d\n", ins
->dreg
, new_var
->dreg
, n
->block_num
);
326 /* The phi nodes are at the beginning of the bblock */
332 for (tmp
= bb
->dominated
; tmp
; tmp
= tmp
->next
) {
333 mono_ssa_rename_vars2 (cfg
, max_vars
, (MonoBasicBlock
*)tmp
->data
, originals_used
, stack
, lvreg_stack
, lvreg_defined
, stack_history
+ stack_history_len
, stack_history_size
- stack_history_len
);
338 for (i
= stack_history_len
- 1; i
>= 0; i
--) {
339 stack
[stack_history
[i
].idx
] = stack_history
[i
].var
;
342 cfg
->comp_done
|= MONO_COMP_SSA_DEF_USE
;
346 mono_ssa_compute2 (MonoCompile
*cfg
)
348 int i
, j
, idx
, bitsize
;
350 MonoMethodVar
*vinfo
= g_new0 (MonoMethodVar
, cfg
->num_varinfo
);
351 MonoInst
*ins
, **stack
;
352 guint8
*buf
, *buf_start
;
353 RenameInfo
*stack_history
;
354 int stack_history_size
;
356 guint32
*lvreg_stack
;
357 gboolean
*lvreg_defined
;
359 g_assert (!(cfg
->comp_done
& MONO_COMP_SSA
));
361 /* we dont support methods containing exception clauses */
362 g_assert (mono_method_get_header (cfg
->method
)->num_clauses
== 0);
363 g_assert (!cfg
->disable_ssa
);
365 if (cfg
->verbose_level
>= 4)
366 printf ("\nCOMPUTE SSA %d (R%d-)\n\n", cfg
->num_varinfo
, cfg
->next_vreg
);
368 #ifdef CREATE_PRUNED_SSA
369 /* we need liveness for pruned SSA */
370 if (!(cfg
->comp_done
& MONO_COMP_LIVENESS
))
371 mono_analyze_liveness (cfg
);
374 mono_compile_dominator_info (cfg
, MONO_COMP_DOM
| MONO_COMP_IDOM
| MONO_COMP_DFRONTIER
);
376 bitsize
= mono_bitset_alloc_size (cfg
->num_bblocks
, 0);
377 buf
= buf_start
= g_malloc0 (mono_bitset_alloc_size (cfg
->num_bblocks
, 0) * cfg
->num_varinfo
);
379 for (i
= 0; i
< cfg
->num_varinfo
; ++i
) {
380 vinfo
[i
].def_in
= mono_bitset_mem_new (buf
, cfg
->num_bblocks
, 0);
383 /* implicit reference at start */
384 mono_bitset_set_fast (vinfo
[i
].def_in
, 0);
387 for (i
= 0; i
< cfg
->num_bblocks
; ++i
) {
388 MONO_BB_FOR_EACH_INS (cfg
->bblocks
[i
], ins
) {
389 if (ins
->opcode
== OP_NOP
)
392 if (!MONO_IS_STORE_MEMBASE (ins
) && get_vreg_to_inst (cfg
, ins
->dreg
)) {
393 mono_bitset_set_fast (vinfo
[get_vreg_to_inst (cfg
, ins
->dreg
)->inst_c0
].def_in
, i
);
398 /* insert phi functions */
399 for (i
= 0; i
< cfg
->num_varinfo
; ++i
) {
400 MonoInst
*var
= cfg
->varinfo
[i
];
402 #if SIZEOF_VOID_P == 4
403 if (var
->type
== STACK_I8
)
406 if (var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
))
409 /* Most variables have only one definition */
410 if (mono_bitset_count (vinfo
[i
].def_in
) <= 1)
413 set
= mono_compile_iterated_dfrontier (cfg
, vinfo
[i
].def_in
);
415 if (cfg
->verbose_level
>= 4) {
416 if (mono_bitset_count (set
) > 0) {
417 printf ("\tR%d needs PHI functions in ", var
->dreg
);
418 mono_blockset_print (cfg
, set
, "", -1);
422 mono_bitset_foreach_bit (set
, idx
, cfg
->num_bblocks
) {
423 MonoBasicBlock
*bb
= cfg
->bblocks
[idx
];
425 /* fixme: create pruned SSA? we would need liveness information for that */
427 if (bb
== cfg
->bb_exit
)
430 if ((cfg
->comp_done
& MONO_COMP_LIVENESS
) && !mono_bitset_test_fast (bb
->live_in_set
, i
)) {
431 //printf ("%d is not live in BB%d %s\n", i, bb->block_num, mono_method_full_name (cfg->method, TRUE));
435 NEW_PHI (cfg
, ins
, i
);
443 ins
->opcode
= OP_PHI
;
446 ins
->opcode
= OP_FPHI
;
449 ins
->opcode
= MONO_CLASS_IS_SIMD (cfg
, var
->klass
) ? OP_XPHI
: OP_VPHI
;
450 ins
->klass
= var
->klass
;
454 ins
->inst_phi_args
= mono_mempool_alloc0 (cfg
->mempool
, sizeof (int) * (cfg
->bblocks
[idx
]->in_count
+ 1));
455 ins
->inst_phi_args
[0] = cfg
->bblocks
[idx
]->in_count
;
458 for (j
= 0; j
< cfg
->bblocks
[idx
]->in_count
; ++j
)
459 ins
->inst_phi_args
[j
+ 1] = -1;
461 ins
->dreg
= cfg
->varinfo
[i
]->dreg
;
463 mono_bblock_insert_before_ins (bb
, bb
->code
, ins
);
466 printf ("ADD PHI BB%d %s\n", cfg
->bblocks
[idx
]->block_num
, mono_method_full_name (cfg
->method
, TRUE
));
477 stack
= alloca (sizeof (MonoInst
*) * cfg
->num_varinfo
);
478 memset (stack
, 0, sizeof (MonoInst
*) * cfg
->num_varinfo
);
480 lvreg_stack
= g_new0 (guint32
, cfg
->next_vreg
);
481 lvreg_defined
= g_new0 (gboolean
, cfg
->next_vreg
);
482 stack_history_size
= 10240;
483 stack_history
= g_new (RenameInfo
, stack_history_size
);
484 originals
= g_new0 (gboolean
, cfg
->num_varinfo
);
485 mono_ssa_rename_vars2 (cfg
, cfg
->num_varinfo
, cfg
->bb_entry
, originals
, stack
, lvreg_stack
, lvreg_defined
, stack_history
, stack_history_size
);
486 g_free (stack_history
);
489 if (cfg
->verbose_level
>= 4)
490 printf ("\nEND COMPUTE SSA.\n\n");
492 cfg
->comp_done
|= MONO_COMP_SSA
;
496 mono_ssa_remove2 (MonoCompile
*cfg
)
498 MonoInst
*ins
, *var
, *move
;
501 g_assert (cfg
->comp_done
& MONO_COMP_SSA
);
503 for (i
= 0; i
< cfg
->num_bblocks
; ++i
) {
504 MonoBasicBlock
*bb
= cfg
->bblocks
[i
];
506 if (cfg
->verbose_level
>= 4)
507 printf ("\nREMOVE SSA %d:\n", bb
->block_num
);
509 for (ins
= bb
->code
; ins
; ins
= ins
->next
) {
510 if (MONO_IS_PHI (ins
)) {
511 g_assert (ins
->inst_phi_args
[0] == bb
->in_count
);
512 var
= get_vreg_to_inst (cfg
, ins
->dreg
);
514 /* Check for PHI nodes where all the inputs are the same */
515 first
= ins
->inst_phi_args
[1];
517 for (j
= 1; j
< bb
->in_count
; ++j
)
518 if (first
!= ins
->inst_phi_args
[j
+ 1])
521 if ((bb
->in_count
> 1) && (j
== bb
->in_count
)) {
522 ins
->opcode
= op_phi_to_move (ins
->opcode
);
523 if (ins
->opcode
== OP_VMOVE
)
524 g_assert (ins
->klass
);
527 for (j
= 0; j
< bb
->in_count
; j
++) {
528 MonoBasicBlock
*pred
= bb
->in_bb
[j
];
529 int sreg
= ins
->inst_phi_args
[j
+ 1];
531 if (cfg
->verbose_level
>= 4)
532 printf ("\tADD R%d <- R%d in BB%d\n", var
->dreg
, sreg
, pred
->block_num
);
533 if (var
->dreg
!= sreg
) {
534 MONO_INST_NEW (cfg
, move
, op_phi_to_move (ins
->opcode
));
535 if (move
->opcode
== OP_VMOVE
) {
536 g_assert (ins
->klass
);
537 move
->klass
= ins
->klass
;
539 move
->dreg
= var
->dreg
;
541 mono_add_ins_to_end (pred
, move
);
545 ins
->opcode
= OP_NOP
;
552 if (cfg
->verbose_level
>= 4) {
553 for (i
= 0; i
< cfg
->num_bblocks
; ++i
) {
554 MonoBasicBlock
*bb
= cfg
->bblocks
[i
];
556 mono_print_bb (bb
, "AFTER REMOVE SSA:");
561 * Removal of SSA form introduces many copies. To avoid this, we tyry to coalesce
562 * the variables if possible. Since the newly introduced SSA variables don't
563 * have overlapping live ranges (because we don't do agressive optimization), we
564 * can coalesce them into the original variable.
567 for (i
= 0; i
< cfg
->num_bblocks
; ++i
) {
568 MonoBasicBlock
*bb
= cfg
->bblocks
[i
];
570 for (ins
= bb
->code
; ins
; ins
= ins
->next
) {
571 const char *spec
= INS_INFO (ins
->opcode
);
573 if (ins
->opcode
== OP_NOP
)
576 if (spec
[MONO_INST_DEST
] != ' ') {
577 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->dreg
);
580 MonoMethodVar
*vmv
= MONO_VARINFO (cfg
, var
->inst_c0
);
583 * The third condition avoids coalescing with variables eliminated
586 if ((vmv
->reg
!= -1) && (vmv
->idx
!= vmv
->reg
) && (MONO_VARINFO (cfg
, vmv
->reg
)->reg
!= -1)) {
587 printf ("COALESCE: R%d -> R%d\n", ins
->dreg
, cfg
->varinfo
[vmv
->reg
]->dreg
);
588 ins
->dreg
= cfg
->varinfo
[vmv
->reg
]->dreg
;
593 if (spec
[MONO_INST_SRC1
] != ' ') {
594 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->sreg1
);
597 MonoMethodVar
*vmv
= MONO_VARINFO (cfg
, var
->inst_c0
);
599 if ((vmv
->reg
!= -1) && (vmv
->idx
!= vmv
->reg
) && (MONO_VARINFO (cfg
, vmv
->reg
)->reg
!= -1)) {
600 printf ("COALESCE: R%d -> R%d\n", ins
->sreg1
, cfg
->varinfo
[vmv
->reg
]->dreg
);
601 ins
->sreg1
= cfg
->varinfo
[vmv
->reg
]->dreg
;
606 if (spec
[MONO_INST_SRC2
] != ' ') {
607 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->sreg2
);
610 MonoMethodVar
*vmv
= MONO_VARINFO (cfg
, var
->inst_c0
);
612 if ((vmv
->reg
!= -1) && (vmv
->idx
!= vmv
->reg
) && (MONO_VARINFO (cfg
, vmv
->reg
)->reg
!= -1)) {
613 printf ("COALESCE: R%d -> R%d\n", ins
->sreg2
, cfg
->varinfo
[vmv
->reg
]->dreg
);
614 ins
->sreg2
= cfg
->varinfo
[vmv
->reg
]->dreg
;
622 for (i
= 0; i
< cfg
->num_varinfo
; ++i
) {
623 MONO_VARINFO (cfg
, i
)->reg
= -1;
626 if (cfg
->comp_done
& MONO_COMP_REACHABILITY
)
627 unlink_unused_bblocks (cfg
);
629 cfg
->comp_done
&= ~MONO_COMP_LIVENESS
;
631 cfg
->comp_done
&= ~MONO_COMP_SSA
;
635 mono_ssa_create_def_use (MonoCompile
*cfg
)
641 g_assert (!(cfg
->comp_done
& MONO_COMP_SSA_DEF_USE
));
643 for (bb
= cfg
->bb_entry
; bb
; bb
= bb
->next_bb
) {
644 for (ins
= bb
->code
; ins
; ins
= ins
->next
) {
645 const char *spec
= INS_INFO (ins
->opcode
);
648 if (ins
->opcode
== OP_NOP
)
652 if (spec
[MONO_INST_SRC1
] != ' ') {
653 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->sreg1
);
654 if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
)))
655 record_use (cfg
, var
, bb
, ins
);
659 if (spec
[MONO_INST_SRC2
] != ' ') {
660 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->sreg2
);
661 if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
)))
662 record_use (cfg
, var
, bb
, ins
);
665 if (MONO_IS_STORE_MEMBASE (ins
)) {
666 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->dreg
);
667 if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
)))
668 record_use (cfg
, var
, bb
, ins
);
671 if (MONO_IS_PHI (ins
)) {
672 for (i
= ins
->inst_phi_args
[0]; i
> 0; i
--) {
673 g_assert (ins
->inst_phi_args
[i
] != -1);
674 record_use (cfg
, get_vreg_to_inst (cfg
, ins
->inst_phi_args
[i
]), bb
, ins
);
679 if ((spec
[MONO_INST_DEST
] != ' ') && !MONO_IS_STORE_MEMBASE (ins
)) {
680 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->dreg
);
682 if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
))) {
683 info
= MONO_VARINFO (cfg
, var
->inst_c0
);
691 cfg
->comp_done
|= MONO_COMP_SSA_DEF_USE
;
695 mono_ssa_copyprop (MonoCompile
*cfg
)
700 g_assert ((cfg
->comp_done
& MONO_COMP_SSA_DEF_USE
));
702 for (index
= 0; index
< cfg
->num_varinfo
; ++index
) {
703 MonoInst
*var
= cfg
->varinfo
[index
];
704 MonoMethodVar
*info
= MONO_VARINFO (cfg
, index
);
706 if (info
->def
&& (MONO_IS_MOVE (info
->def
))) {
707 MonoInst
*var2
= get_vreg_to_inst (cfg
, info
->def
->sreg1
);
709 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
))) {
710 /* Rewrite all uses of var to be uses of var2 */
711 int dreg
= var
->dreg
;
712 int sreg1
= var2
->dreg
;
717 MonoVarUsageInfo
*u
= (MonoVarUsageInfo
*)l
->data
;
718 MonoInst
*ins
= u
->inst
;
719 GList
*next
= l
->next
;
721 spec
= INS_INFO (ins
->opcode
);
723 if (spec
[MONO_INST_SRC1
] != ' ' && ins
->sreg1
== dreg
) {
725 } else if (spec
[MONO_INST_SRC2
] != ' ' && ins
->sreg2
== dreg
) {
727 } else if (MONO_IS_STORE_MEMBASE (ins
) && ins
->dreg
== dreg
) {
729 } else if (MONO_IS_PHI (ins
)) {
730 for (i
= ins
->inst_phi_args
[0]; i
> 0; i
--) {
731 int sreg
= ins
->inst_phi_args
[i
];
732 if (sreg
== var
->dreg
)
736 ins
->inst_phi_args
[i
] = sreg1
;
739 g_assert_not_reached ();
741 record_use (cfg
, var2
, u
->bb
, ins
);
751 if (cfg
->verbose_level
>= 4) {
754 for (bb
= cfg
->bb_entry
; bb
; bb
= bb
->next_bb
)
755 mono_print_bb (bb
, "AFTER SSA COPYPROP");
760 evaluate_ins (MonoCompile
*cfg
, MonoInst
*ins
, MonoInst
**res
, MonoInst
**carray
)
762 MonoInst
*arg0
, *arg1
, *c0
;
764 gboolean const_args
= FALSE
;
765 const char *spec
= INS_INFO (ins
->opcode
);
767 /* Short-circuit this */
768 if (ins
->opcode
== OP_ICONST
) {
773 if (ins
->opcode
== OP_NOP
)
777 if (spec
[MONO_INST_SRC1
] != ' ') {
778 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->sreg1
);
781 arg0
= carray
[ins
->sreg1
];
784 else if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
)))
785 r1
= MONO_VARINFO (cfg
, var
->inst_c0
)->cpstate
;
791 if (spec
[MONO_INST_SRC2
] != ' ') {
792 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->sreg2
);
795 arg1
= carray
[ins
->sreg2
];
798 else if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
)))
799 r2
= MONO_VARINFO (cfg
, var
->inst_c0
)->cpstate
;
806 if ((spec
[MONO_INST_SRC1
] != ' ') && (spec
[MONO_INST_SRC2
] != ' ')) {
808 const_args
= (r1
== 1) && (r2
== 1);
810 else if (spec
[MONO_INST_SRC1
] != ' ') {
812 const_args
= (r1
== 1);
816 if ((spec
[MONO_INST_DEST
] != ' ') && carray
[ins
->dreg
]) {
818 *res
= carray
[ins
->dreg
];
821 c0
= mono_constant_fold_ins2 (cfg
, ins
, arg0
, arg1
, FALSE
);
823 if (G_UNLIKELY (cfg
->verbose_level
> 1)) {
824 printf ("\t cfold -> ");
831 /* Can't cfold this ins */
835 if ((spec
[MONO_INST_SRC1
] != ' ') && (spec
[MONO_INST_SRC2
] != ' ')) {
837 if ((r1
== 2) || (r2
== 2))
842 else if (spec
[MONO_INST_SRC1
] != ' ') {
854 change_varstate (MonoCompile
*cfg
, GList
**cvars
, MonoMethodVar
*info
, int state
, MonoInst
*c0
, MonoInst
**carray
)
856 if (info
->cpstate
>= state
)
859 info
->cpstate
= state
;
861 if (G_UNLIKELY (cfg
->verbose_level
> 1))
862 printf ("\tState of R%d set to %d\n", cfg
->varinfo
[info
->idx
]->dreg
, info
->cpstate
);
867 carray
[cfg
->varinfo
[info
->idx
]->dreg
] = c0
;
869 if (!g_list_find (*cvars
, info
)) {
870 *cvars
= g_list_prepend (*cvars
, info
);
875 add_cprop_bb (MonoCompile
*cfg
, MonoBasicBlock
*bb
, GList
**bblist
)
877 if (G_UNLIKELY (cfg
->verbose_level
> 1))
878 printf ("\tAdd BB%d to worklist\n", bb
->block_num
);
880 if (!(bb
->flags
& BB_REACHABLE
)) {
881 bb
->flags
|= BB_REACHABLE
;
882 *bblist
= g_list_prepend (*bblist
, bb
);
887 visit_inst (MonoCompile
*cfg
, MonoBasicBlock
*bb
, MonoInst
*ins
, GList
**cvars
, GList
**bblist
, MonoInst
**carray
)
889 const char *spec
= INS_INFO (ins
->opcode
);
891 if (ins
->opcode
== OP_NOP
)
894 if (cfg
->verbose_level
> 1)
895 mono_print_ins (ins
);
897 /* FIXME: Support longs/floats */
898 /* FIXME: Work on vregs as well */
900 if (MONO_IS_PHI (ins
)) {
901 MonoMethodVar
*info
= MONO_VARINFO (cfg
, get_vreg_to_inst (cfg
, ins
->dreg
)->inst_c0
);
905 for (j
= 1; j
<= ins
->inst_phi_args
[0]; j
++) {
906 MonoInst
*var
= get_vreg_to_inst (cfg
, ins
->inst_phi_args
[j
]);
907 MonoMethodVar
*mv
= MONO_VARINFO (cfg
, var
->inst_c0
);
908 MonoInst
*src
= mv
->def
;
910 if (mv
->def_bb
&& !(mv
->def_bb
->flags
& BB_REACHABLE
))
913 if (!mv
->def
|| !src
|| mv
->cpstate
== 2) {
914 change_varstate (cfg
, cvars
, info
, 2, NULL
, carray
);
918 if (mv
->cpstate
== 0)
921 g_assert (carray
[var
->dreg
]);
924 c0
= carray
[var
->dreg
];
927 if (c0
->opcode
!= OP_ICONST
) {
928 change_varstate (cfg
, cvars
, info
, 2, NULL
, carray
);
932 if (carray
[var
->dreg
]->inst_c0
!= c0
->inst_c0
) {
933 change_varstate (cfg
, cvars
, info
, 2, NULL
, carray
);
938 if (c0
&& info
->cpstate
< 1) {
939 change_varstate (cfg
, cvars
, info
, 1, c0
, carray
);
941 g_assert (c0
->opcode
== OP_ICONST
);
944 else if (!MONO_IS_STORE_MEMBASE (ins
) && ((spec
[MONO_INST_SRC1
] != ' ') || (spec
[MONO_INST_SRC2
] != ' ') || (spec
[MONO_INST_DEST
] != ' '))) {
948 if (spec
[MONO_INST_DEST
] != ' ')
949 var
= get_vreg_to_inst (cfg
, ins
->dreg
);
954 state
= evaluate_ins (cfg
, ins
, &c0
, carray
);
956 if (var
&& !(var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
))) {
957 MonoMethodVar
*info
= MONO_VARINFO (cfg
, var
->inst_c0
);
959 if (info
->cpstate
< 2) {
961 change_varstate (cfg
, cvars
, info
, 1, c0
, carray
);
963 change_varstate (cfg
, cvars
, info
, 2, NULL
, carray
);
966 else if (!var
&& (ins
->dreg
!= -1)) {
968 * We don't record def-use information for local vregs since it would be
969 * expensive. Instead, we depend on the fact that all uses of the vreg are in
970 * the same bblock, so they will be examined after the definition.
971 * FIXME: This isn't true if the ins is visited through an SSA edge.
974 carray
[ins
->dreg
] = c0
;
976 if (carray
[ins
->dreg
]) {
978 * The state of the vreg changed from constant to non-constant
979 * -> need to rescan the whole bblock.
981 carray
[ins
->dreg
] = NULL
;
982 /* FIXME: Speed this up */
984 if (!g_list_find (*bblist
, bb
))
985 *bblist
= g_list_prepend (*bblist
, bb
);
990 if (ins
->opcode
== OP_JUMP_TABLE
) {
992 MonoJumpInfoBBTable
*table
= ins
->inst_p0
;
994 if (ins
->next
->opcode
!= OP_PADD
) {
995 /* The PADD was optimized away */
996 /* FIXME: handle this as well */
997 for (i
= 0; i
< table
->table_size
; i
++)
998 if (table
->table
[i
])
999 add_cprop_bb (cfg
, table
->table
[i
], bblist
);
1003 g_assert (ins
->next
->opcode
== OP_PADD
);
1004 g_assert (ins
->next
->sreg1
== ins
->dreg
);
1006 if (carray
[ins
->next
->sreg2
]) {
1007 #if SIZEOF_VOID_P == 8
1008 int idx
= carray
[ins
->next
->sreg2
]->inst_c0
>> 3;
1010 int idx
= carray
[ins
->next
->sreg2
]->inst_c0
>> 2;
1012 if ((idx
< 0) || (idx
>= table
->table_size
))
1013 /* Out-of-range, no branch is executed */
1016 if (table
->table
[idx
])
1017 add_cprop_bb (cfg
, table
->table
[idx
], bblist
);
1020 for (i
= 0; i
< table
->table_size
; i
++)
1021 if (table
->table
[i
])
1022 add_cprop_bb (cfg
, table
->table
[i
], bblist
);
1026 if (ins
->opcode
== OP_SWITCH
) {
1028 MonoJumpInfoBBTable
*table
= ins
->inst_p0
;
1030 for (i
= 0; i
< table
->table_size
; i
++)
1031 if (table
->table
[i
])
1032 add_cprop_bb (cfg
, table
->table
[i
], bblist
);
1035 /* Handle COMPARE+BRCOND pairs */
1036 if (ins
->next
&& MONO_IS_COND_BRANCH_OP (ins
->next
)) {
1038 g_assert (c0
->opcode
== OP_ICONST
);
1041 ins
->next
->flags
|= MONO_INST_CFOLD_TAKEN
;
1043 ins
->next
->flags
|= MONO_INST_CFOLD_NOT_TAKEN
;
1046 ins
->next
->flags
&= ~(MONO_INST_CFOLD_TAKEN
| MONO_INST_CFOLD_NOT_TAKEN
);
1049 visit_inst (cfg
, bb
, ins
->next
, cvars
, bblist
, carray
);
1051 } else if (ins
->opcode
== OP_BR
) {
1052 add_cprop_bb (cfg
, ins
->inst_target_bb
, bblist
);
1053 } else if (MONO_IS_COND_BRANCH_OP (ins
)) {
1054 if (ins
->flags
& MONO_INST_CFOLD_TAKEN
) {
1055 add_cprop_bb (cfg
, ins
->inst_true_bb
, bblist
);
1056 } else if (ins
->flags
& MONO_INST_CFOLD_NOT_TAKEN
) {
1057 if (ins
->inst_false_bb
)
1058 add_cprop_bb (cfg
, ins
->inst_false_bb
, bblist
);
1060 add_cprop_bb (cfg
, ins
->inst_true_bb
, bblist
);
1061 if (ins
->inst_false_bb
)
1062 add_cprop_bb (cfg
, ins
->inst_false_bb
, bblist
);
1070 * Replace INS with its constant value, if it exists
1073 fold_ins (MonoCompile
*cfg
, MonoBasicBlock
*bb
, MonoInst
*ins
, MonoInst
**carray
)
1075 const char *spec
= INS_INFO (ins
->opcode
);
1078 if ((ins
->opcode
!= OP_NOP
) && (ins
->dreg
!= -1) && !MONO_IS_STORE_MEMBASE (ins
)) {
1079 if (carray
[ins
->dreg
] && (spec
[MONO_INST_DEST
] == 'i') && (ins
->dreg
>= MONO_MAX_IREGS
)) {
1080 /* Perform constant folding */
1082 g_assert (carray
[ins
->dreg
]->opcode
== OP_ICONST
);
1083 ins
->opcode
= OP_ICONST
;
1084 ins
->inst_c0
= carray
[ins
->dreg
]->inst_c0
;
1085 ins
->sreg1
= ins
->sreg2
= -1;
1087 else if ((spec
[MONO_INST_SRC2
] != ' ') && carray
[ins
->sreg2
]) {
1088 /* Perform op->op_imm conversion */
1089 opcode2
= mono_op_to_op_imm (ins
->opcode
);
1090 if (opcode2
!= -1) {
1091 ins
->opcode
= opcode2
;
1092 ins
->inst_imm
= carray
[ins
->sreg2
]->inst_c0
;
1095 if ((opcode2
== OP_VOIDCALL
) || (opcode2
== OP_CALL
) || (opcode2
== OP_LCALL
) || (opcode2
== OP_FCALL
))
1096 ((MonoCallInst
*)ins
)->fptr
= (gpointer
)ins
->inst_imm
;
1100 if (ins
->opcode
== OP_JUMP_TABLE
) {
1102 MonoJumpInfoBBTable
*table
= ins
->inst_p0
;
1104 if (ins
->next
->opcode
!= OP_PADD
) {
1105 /* The PADD was optimized away */
1106 /* FIXME: handle this as well */
1110 g_assert (ins
->next
->opcode
== OP_PADD
);
1111 g_assert (ins
->next
->sreg1
== ins
->dreg
);
1112 g_assert (ins
->next
->next
->opcode
== OP_LOAD_MEMBASE
);
1114 if (carray
[ins
->next
->sreg2
]) {
1115 /* Convert to a simple branch */
1116 #if SIZEOF_VOID_P == 8
1117 int idx
= carray
[ins
->next
->sreg2
]->inst_c0
>> 3;
1119 int idx
= carray
[ins
->next
->sreg2
]->inst_c0
>> 2;
1122 if (!((idx
>= 0) && (idx
< table
->table_size
))) {
1123 /* Out of range, eliminate the whole switch */
1124 for (i
= 0; i
< table
->table_size
; ++i
) {
1125 remove_bb_from_phis (cfg
, bb
, table
->table
[i
]);
1126 mono_unlink_bblock (cfg
, bb
, table
->table
[i
]);
1130 NULLIFY_INS (ins
->next
);
1131 NULLIFY_INS (ins
->next
->next
);
1132 if (ins
->next
->next
->next
)
1133 NULLIFY_INS (ins
->next
->next
->next
);
1138 if (!ins
->next
->next
->next
|| ins
->next
->next
->next
->opcode
!= OP_BR_REG
) {
1139 /* A one-way switch which got optimized away */
1140 if (G_UNLIKELY (cfg
->verbose_level
> 1)) {
1141 printf ("\tNo cfold on ");
1142 mono_print_ins (ins
);
1147 if (G_UNLIKELY (cfg
->verbose_level
> 1)) {
1148 printf ("\tcfold on ");
1149 mono_print_ins (ins
);
1152 /* Unlink target bblocks */
1153 for (i
= 0; i
< table
->table_size
; ++i
) {
1155 remove_bb_from_phis (cfg
, bb
, table
->table
[i
]);
1156 mono_unlink_bblock (cfg
, bb
, table
->table
[i
]);
1160 /* Change the OP_BR_REG to a simple branch */
1161 ins
->next
->next
->next
->opcode
= OP_BR
;
1162 ins
->next
->next
->next
->inst_target_bb
= table
->table
[idx
];
1163 ins
->next
->next
->next
->sreg1
= -1;
1165 /* Nullify the other instructions */
1167 NULLIFY_INS (ins
->next
);
1168 NULLIFY_INS (ins
->next
->next
);
1172 else if (MONO_IS_COND_BRANCH_OP (ins
)) {
1173 if (ins
->flags
& MONO_INST_CFOLD_TAKEN
) {
1174 remove_bb_from_phis (cfg
, bb
, ins
->inst_false_bb
);
1175 mono_unlink_bblock (cfg
, bb
, ins
->inst_false_bb
);
1176 ins
->opcode
= OP_BR
;
1177 ins
->inst_target_bb
= ins
->inst_true_bb
;
1178 } else if (ins
->flags
& MONO_INST_CFOLD_NOT_TAKEN
) {
1179 remove_bb_from_phis (cfg
, bb
, ins
->inst_true_bb
);
1180 mono_unlink_bblock (cfg
, bb
, ins
->inst_true_bb
);
1181 ins
->opcode
= OP_BR
;
1182 ins
->inst_target_bb
= ins
->inst_false_bb
;
1188 mono_ssa_cprop2 (MonoCompile
*cfg
)
1192 GList
*bblock_list
, *cvars
;
1195 //printf ("SIMPLE OPTS BB%d %s\n", bb->block_num, mono_method_full_name (cfg->method, TRUE));
1197 carray
= g_new0 (MonoInst
*, cfg
->next_vreg
);
1199 if (!(cfg
->comp_done
& MONO_COMP_SSA_DEF_USE
))
1200 mono_ssa_create_def_use (cfg
);
1202 bblock_list
= g_list_prepend (NULL
, cfg
->bb_entry
);
1203 cfg
->bb_entry
->flags
|= BB_REACHABLE
;
1205 memset (carray
, 0, sizeof (MonoInst
*) * cfg
->num_varinfo
);
1207 for (i
= 0; i
< cfg
->num_varinfo
; i
++) {
1208 MonoMethodVar
*info
= MONO_VARINFO (cfg
, i
);
1215 while (bblock_list
) {
1218 bb
= (MonoBasicBlock
*)bblock_list
->data
;
1220 bblock_list
= g_list_delete_link (bblock_list
, bblock_list
);
1222 g_assert (bb
->flags
& BB_REACHABLE
);
1224 if (bb
->out_count
== 1) {
1225 if (!(bb
->out_bb
[0]->flags
& BB_REACHABLE
)) {
1226 bb
->out_bb
[0]->flags
|= BB_REACHABLE
;
1227 bblock_list
= g_list_prepend (bblock_list
, bb
->out_bb
[0]);
1231 if (cfg
->verbose_level
> 1)
1232 printf ("\nSSA CONSPROP BB%d:\n", bb
->block_num
);
1234 for (inst
= bb
->code
; inst
; inst
= inst
->next
) {
1235 visit_inst (cfg
, bb
, inst
, &cvars
, &bblock_list
, carray
);
1239 MonoMethodVar
*info
= (MonoMethodVar
*)cvars
->data
;
1240 cvars
= g_list_delete_link (cvars
, cvars
);
1242 for (tmp
= info
->uses
; tmp
; tmp
= tmp
->next
) {
1243 MonoVarUsageInfo
*ui
= (MonoVarUsageInfo
*)tmp
->data
;
1244 if (!(ui
->bb
->flags
& BB_REACHABLE
))
1246 visit_inst (cfg
, ui
->bb
, ui
->inst
, &cvars
, &bblock_list
, carray
);
1251 for (bb
= cfg
->bb_entry
->next_bb
; bb
; bb
= bb
->next_bb
) {
1253 for (inst
= bb
->code
; inst
; inst
= inst
->next
) {
1254 fold_ins (cfg
, bb
, inst
, carray
);
1260 cfg
->comp_done
|= MONO_COMP_REACHABILITY
;
1262 /* fixme: we should update usage infos during cprop, instead of computing it again */
1263 cfg
->comp_done
&= ~MONO_COMP_SSA_DEF_USE
;
1264 for (i
= 0; i
< cfg
->num_varinfo
; i
++) {
1265 MonoMethodVar
*info
= MONO_VARINFO (cfg
, i
);
1272 add_to_dce_worklist (MonoCompile
*cfg
, MonoMethodVar
*var
, MonoMethodVar
*use
, GList
**wl
)
1276 *wl
= g_list_prepend_mempool (cfg
->mempool
, *wl
, use
);
1278 for (tmp
= use
->uses
; tmp
; tmp
= tmp
->next
) {
1279 MonoVarUsageInfo
*ui
= (MonoVarUsageInfo
*)tmp
->data
;
1280 if (ui
->inst
== var
->def
) {
1281 /* from the mempool */
1282 use
->uses
= g_list_remove_link (use
->uses
, tmp
);
1289 mono_ssa_deadce2 (MonoCompile
*cfg
)
1294 g_assert (cfg
->comp_done
& MONO_COMP_SSA
);
1296 //printf ("DEADCE %s\n", mono_method_full_name (cfg->method, TRUE));
1298 if (!(cfg
->comp_done
& MONO_COMP_SSA_DEF_USE
))
1299 mono_ssa_create_def_use (cfg
);
1301 mono_ssa_copyprop (cfg
);
1304 for (i
= 0; i
< cfg
->num_varinfo
; i
++) {
1305 MonoMethodVar
*info
= MONO_VARINFO (cfg
, i
);
1306 work_list
= g_list_prepend_mempool (cfg
->mempool
, work_list
, info
);
1310 MonoMethodVar
*info
= (MonoMethodVar
*)work_list
->data
;
1311 work_list
= g_list_remove_link (work_list
, work_list
);
1314 * The second part of the condition happens often when PHI nodes have their dreg
1315 * as one of their arguments due to the fact that we use the original vars.
1317 if (info
->def
&& (!info
->uses
|| ((info
->uses
->next
== NULL
) && (((MonoVarUsageInfo
*)info
->uses
->data
)->inst
== info
->def
)))) {
1318 MonoInst
*def
= info
->def
;
1320 /* Eliminating FMOVE could screw up the fp stack */
1321 if (MONO_IS_MOVE (def
) && (!MONO_ARCH_USE_FPSTACK
|| (def
->opcode
!= OP_FMOVE
))) {
1322 MonoInst
*src_var
= get_vreg_to_inst (cfg
, def
->sreg1
);
1323 if (src_var
&& !(src_var
->flags
& (MONO_INST_VOLATILE
|MONO_INST_INDIRECT
)))
1324 add_to_dce_worklist (cfg
, info
, MONO_VARINFO (cfg
, src_var
->inst_c0
), &work_list
);
1325 def
->opcode
= OP_NOP
;
1326 def
->dreg
= def
->sreg1
= def
->sreg2
= -1;
1328 } else if ((def
->opcode
== OP_ICONST
) || (def
->opcode
== OP_I8CONST
) || MONO_IS_ZERO (def
)) {
1329 def
->opcode
= OP_NOP
;
1330 def
->dreg
= def
->sreg1
= def
->sreg2
= -1;
1332 } else if (MONO_IS_PHI (def
)) {
1334 for (j
= def
->inst_phi_args
[0]; j
> 0; j
--) {
1335 MonoMethodVar
*u
= MONO_VARINFO (cfg
, get_vreg_to_inst (cfg
, def
->inst_phi_args
[j
])->inst_c0
);
1336 add_to_dce_worklist (cfg
, info
, u
, &work_list
);
1338 def
->opcode
= OP_NOP
;
1339 def
->dreg
= def
->sreg1
= def
->sreg2
= -1;
1342 else if (def
->opcode
== OP_NOP
) {
1345 //mono_print_ins (def);
1353 mono_ssa_strength_reduction (MonoCompile
*cfg
)
1358 g_assert (cfg
->comp_done
& MONO_COMP_SSA
);
1359 g_assert (cfg
->comp_done
& MONO_COMP_LOOPS
);
1360 g_assert (cfg
->comp_done
& MONO_COMP_SSA_DEF_USE
);
1362 for (bb
= cfg
->bb_entry
->next_bb
; bb
; bb
= bb
->next_bb
) {
1363 GList
*lp
= bb
->loop_blocks
;
1366 MonoBasicBlock
*h
= (MonoBasicBlock
*)lp
->data
;
1368 /* we only consider loops with 2 in bblocks */
1369 if (!h
->in_count
== 2)
1372 for (i
= 0; i
< cfg
->num_varinfo
; i
++) {
1373 MonoMethodVar
*info
= MONO_VARINFO (cfg
, i
);
1375 if (info
->def
&& info
->def
->ssa_op
== MONO_SSA_STORE
&&
1376 info
->def
->inst_i0
->opcode
== OP_LOCAL
&& g_list_find (lp
, info
->def_bb
)) {
1377 MonoInst
*v
= info
->def
->inst_i1
;
1380 printf ("FOUND %d in %s\n", info
->idx
, mono_method_full_name (cfg
->method
, TRUE
));
1388 #endif /* DISABLE_JIT */