3 * Implement simple alias analysis for local variables.
6 * Rodrigo Kumpera (kumpera@gmail.com)
17 #include <mono/utils/mono-compiler.h>
22 is_int_stack_size (int type
)
24 #if TARGET_SIZEOF_VOID_P == 4
25 return type
== STACK_I4
|| type
== STACK_MP
|| type
== STACK_PTR
;
27 return type
== STACK_I4
;
32 is_long_stack_size (int type
)
34 #if TARGET_SIZEOF_VOID_P == 8
35 return type
== STACK_I8
|| type
== STACK_MP
|| type
== STACK_PTR
;
37 return type
== STACK_I8
;
43 lower_load (MonoCompile
*cfg
, MonoInst
*load
, MonoInst
*ldaddr
)
45 MonoInst
*var
= (MonoInst
*)ldaddr
->inst_p0
;
46 MonoType
*type
= m_class_get_byval_arg (var
->klass
);
47 int replaced_op
= mono_type_to_load_membase (cfg
, type
);
49 if (load
->opcode
== OP_LOADV_MEMBASE
&& load
->klass
!= var
->klass
) {
50 if (cfg
->verbose_level
> 2)
51 printf ("Incompatible load_vtype classes %s x %s\n", m_class_get_name (load
->klass
), m_class_get_name (var
->klass
));
55 if (replaced_op
!= load
->opcode
) {
56 if (cfg
->verbose_level
> 2)
57 printf ("Incompatible load type: expected %s but got %s\n",
58 mono_inst_name (replaced_op
),
59 mono_inst_name (load
->opcode
));
62 if (cfg
->verbose_level
> 2) { printf ("mem2reg replacing: "); mono_print_ins (load
); }
65 load
->opcode
= mono_type_to_regmove (cfg
, type
);
66 mini_type_to_eval_stack_type (cfg
, type
, load
);
67 load
->sreg1
= var
->dreg
;
68 mono_atomic_inc_i32 (&mono_jit_stats
.loads_eliminated
);
73 lower_store (MonoCompile
*cfg
, MonoInst
*store
, MonoInst
*ldaddr
)
75 MonoInst
*var
= (MonoInst
*)ldaddr
->inst_p0
;
76 MonoType
*type
= m_class_get_byval_arg (var
->klass
);
77 int replaced_op
= mono_type_to_store_membase (cfg
, type
);
79 if (store
->opcode
== OP_STOREV_MEMBASE
&& store
->klass
!= var
->klass
) {
80 if (cfg
->verbose_level
> 2)
81 printf ("Incompatible store_vtype classes %s x %s\n", m_class_get_name (store
->klass
), m_class_get_name (store
->klass
));
86 if (replaced_op
!= store
->opcode
) {
87 if (cfg
->verbose_level
> 2)
88 printf ("Incompatible store_reg type: expected %s but got %s\n",
89 mono_inst_name (replaced_op
),
90 mono_inst_name (store
->opcode
));
93 if (cfg
->verbose_level
> 2) { printf ("mem2reg replacing: "); mono_print_ins (store
); }
96 int coerce_op
= mono_type_to_stloc_coerce (type
);
98 store
->opcode
= coerce_op
;
100 store
->opcode
= mono_type_to_regmove (cfg
, type
);
101 mini_type_to_eval_stack_type (cfg
, type
, store
);
102 store
->dreg
= var
->dreg
;
103 mono_atomic_inc_i32 (&mono_jit_stats
.stores_eliminated
);
108 lower_store_imm (MonoCompile
*cfg
, MonoInst
*store
, MonoInst
*ldaddr
)
110 MonoInst
*var
= (MonoInst
*)ldaddr
->inst_p0
;
111 MonoType
*type
= m_class_get_byval_arg (var
->klass
);
112 int store_op
= mono_type_to_store_membase (cfg
, type
);
113 if (store_op
== OP_STOREV_MEMBASE
|| store_op
== OP_STOREX_MEMBASE
)
116 switch (store
->opcode
) {
117 #if TARGET_SIZEOF_VOID_P == 4
118 case OP_STORE_MEMBASE_IMM
:
120 case OP_STOREI4_MEMBASE_IMM
:
121 if (!is_int_stack_size (var
->type
)) {
122 if (cfg
->verbose_level
> 2) printf ("Incompatible variable of size != 4\n");
125 if (cfg
->verbose_level
> 2) { printf ("mem2reg replacing: "); mono_print_ins (store
); }
126 store
->opcode
= OP_ICONST
;
127 store
->type
= STACK_I4
;
128 store
->dreg
= var
->dreg
;
129 store
->inst_c0
= store
->inst_imm
;
132 #if TARGET_SIZEOF_VOID_P == 8
133 case OP_STORE_MEMBASE_IMM
:
135 case OP_STOREI8_MEMBASE_IMM
:
136 if (!is_long_stack_size (var
->type
)) {
137 if (cfg
->verbose_level
> 2) printf ("Incompatible variable of size != 8\n");
140 if (cfg
->verbose_level
> 2) { printf ("mem2reg replacing: "); mono_print_ins (store
); }
141 store
->opcode
= OP_I8CONST
;
142 store
->type
= STACK_I8
;
143 store
->dreg
= var
->dreg
;
144 store
->inst_l
= store
->inst_imm
;
149 mono_atomic_inc_i32 (&mono_jit_stats
.stores_eliminated
);
154 kill_call_arg_alias (MonoCompile
*cfg
, GHashTable
*addr_loads
, GSList
*l
)
156 for (; l
; l
= l
->next
) {
158 guint32 regpair
, reg
;
160 regpair
= (guint32
)(gssize
)(l
->data
);
161 reg
= regpair
& 0xffffff;
163 tmp
= (MonoInst
*)g_hash_table_lookup (addr_loads
, GINT_TO_POINTER (reg
));
165 // This call passes an alias as an argument. This means that the contents
166 // of the passed pointer can change. If the content is also an alias then
167 // we need to forget it as we do for moves.
168 if (g_hash_table_remove (addr_loads
, GINT_TO_POINTER (((MonoInst
*)tmp
->inst_p0
)->dreg
))) {
169 if (cfg
->verbose_level
> 2)
170 printf ("Killed alias %d\n", ((MonoInst
*)tmp
->inst_p0
)->dreg
);
178 lower_memory_access (MonoCompile
*cfg
)
182 gboolean needs_dce
= FALSE
;
183 GHashTable
*addr_loads
= g_hash_table_new (NULL
, NULL
);
185 for (bb
= cfg
->bb_entry
; bb
; bb
= bb
->next_bb
) {
186 g_hash_table_remove_all (addr_loads
);
188 for (ins
= bb
->code
; ins
; ins
= ins
->next
) {
190 switch (ins
->opcode
) {
192 MonoInst
*var
= (MonoInst
*)ins
->inst_p0
;
193 if (var
->flags
& MONO_INST_VOLATILE
) {
194 if (cfg
->verbose_level
> 2) { printf ("Found address to volatile var, can't take it: "); mono_print_ins (ins
); }
196 g_hash_table_insert (addr_loads
, GINT_TO_POINTER (ins
->dreg
), ins
);
197 if (cfg
->verbose_level
> 2) { printf ("New address: "); mono_print_ins (ins
); }
203 tmp
= (MonoInst
*)g_hash_table_lookup (addr_loads
, GINT_TO_POINTER (ins
->sreg1
));
205 Forward propagate known aliases
210 g_hash_table_insert (addr_loads
, GINT_TO_POINTER (ins
->dreg
), tmp
);
211 if (cfg
->verbose_level
> 2) { printf ("New alias: "); mono_print_ins (ins
); }
214 Source value is not a know address, kill the variable.
216 if (g_hash_table_remove (addr_loads
, GINT_TO_POINTER (ins
->dreg
))) {
217 if (cfg
->verbose_level
> 2) { printf ("Killed alias: "); mono_print_ins (ins
); }
222 case OP_LOADV_MEMBASE
:
223 case OP_LOAD_MEMBASE
:
224 case OP_LOADU1_MEMBASE
:
225 case OP_LOADI2_MEMBASE
:
226 case OP_LOADU2_MEMBASE
:
227 case OP_LOADI4_MEMBASE
:
228 case OP_LOADU4_MEMBASE
:
229 case OP_LOADI1_MEMBASE
:
230 case OP_LOADI8_MEMBASE
:
231 #ifndef MONO_ARCH_SOFT_FLOAT_FALLBACK
232 case OP_LOADR4_MEMBASE
:
234 case OP_LOADR8_MEMBASE
:
235 if (ins
->inst_offset
!= 0)
237 tmp
= (MonoInst
*)g_hash_table_lookup (addr_loads
, GINT_TO_POINTER (ins
->sreg1
));
239 if (cfg
->verbose_level
> 2) { printf ("Found candidate load:"); mono_print_ins (ins
); }
240 if (lower_load (cfg
, ins
, tmp
)) {
242 /* Try to propagate known aliases if an OP_MOVE was inserted */
243 goto handle_instruction
;
248 case OP_STORE_MEMBASE_REG
:
249 case OP_STOREI1_MEMBASE_REG
:
250 case OP_STOREI2_MEMBASE_REG
:
251 case OP_STOREI4_MEMBASE_REG
:
252 case OP_STOREI8_MEMBASE_REG
:
253 #ifndef MONO_ARCH_SOFT_FLOAT_FALLBACK
254 case OP_STORER4_MEMBASE_REG
:
256 case OP_STORER8_MEMBASE_REG
:
257 case OP_STOREV_MEMBASE
:
258 if (ins
->inst_offset
!= 0)
260 tmp
= (MonoInst
*)g_hash_table_lookup (addr_loads
, GINT_TO_POINTER (ins
->dreg
));
262 if (cfg
->verbose_level
> 2) { printf ("Found candidate store:"); mono_print_ins (ins
); }
263 if (lower_store (cfg
, ins
, tmp
)) {
265 /* Try to propagate known aliases if an OP_MOVE was inserted */
266 goto handle_instruction
;
270 //FIXME missing storei1_membase_imm and storei2_membase_imm
271 case OP_STORE_MEMBASE_IMM
:
272 case OP_STOREI4_MEMBASE_IMM
:
273 case OP_STOREI8_MEMBASE_IMM
:
274 if (ins
->inst_offset
!= 0)
276 tmp
= (MonoInst
*)g_hash_table_lookup (addr_loads
, GINT_TO_POINTER (ins
->dreg
));
278 if (cfg
->verbose_level
> 2) { printf ("Found candidate store-imm:"); mono_print_ins (ins
); }
279 needs_dce
|= lower_store_imm (cfg
, ins
, tmp
);
284 tmp
= (MonoInst
*)g_hash_table_lookup (addr_loads
, GINT_TO_POINTER (ins
->sreg1
));
286 if (cfg
->verbose_level
> 2) { printf ("Found null check over local: "); mono_print_ins (ins
); }
292 if (MONO_IS_CALL (ins
)) {
293 MonoCallInst
*call
= (MonoCallInst
*)ins
;
295 kill_call_arg_alias (cfg
, addr_loads
, call
->out_ireg_args
);
297 // FIXME Kill more aliases if used as dreg, since we are not in ssa form.
298 // This would need some optimizations so we don't lookup hash table for every
305 g_hash_table_destroy (addr_loads
);
307 #ifdef ENABLE_NETCORE
308 /* There could be ldaddr instructions which already got eliminated */
309 if (COMPILE_LLVM (cfg
))
316 recompute_aliased_variables (MonoCompile
*cfg
, int *restored_vars
)
325 for (i
= 0; i
< cfg
->num_varinfo
; i
++) {
326 MonoInst
*var
= cfg
->varinfo
[i
];
327 if (var
->flags
& MONO_INST_INDIRECT
) {
328 if (cfg
->verbose_level
> 2) {
329 printf ("Killing :"); mono_print_ins (var
);
333 var
->flags
&= ~MONO_INST_INDIRECT
;
339 for (bb
= cfg
->bb_entry
; bb
; bb
= bb
->next_bb
) {
340 for (ins
= bb
->code
; ins
; ins
= ins
->next
) {
341 if (ins
->opcode
== OP_LDADDR
) {
344 if (cfg
->verbose_level
> 2) { printf ("Found op :"); mono_print_ins (ins
); }
346 var
= (MonoInst
*)ins
->inst_p0
;
347 if (!(var
->flags
& MONO_INST_INDIRECT
)) {
348 if (cfg
->verbose_level
> 1) { printf ("Restoring :"); mono_print_ins (var
); }
351 var
->flags
|= MONO_INST_INDIRECT
;
355 *restored_vars
= adds
;
357 mono_atomic_fetch_add_i32 (&mono_jit_stats
.alias_found
, kills
);
358 mono_atomic_fetch_add_i32 (&mono_jit_stats
.alias_removed
, kills
- adds
);
360 if (cfg
->verbose_level
> 2) {
361 printf ("Method: %s\n", mono_method_full_name (cfg
->method
, 1));
362 printf ("Kills %d Adds %d\n", kills
, adds
);
371 Don't DCE on the whole CFG, only the BBs that have changed.
374 SRVT of small types can fix cases of mismatch for fields of a different type than the component.
375 Handle aliasing of byrefs in call conventions.
378 mono_local_alias_analysis (MonoCompile
*cfg
)
380 int i
, restored_vars
= 1;
381 if (!cfg
->has_indirection
)
384 if (cfg
->verbose_level
> 2)
385 mono_print_code (cfg
, "BEFORE ALIAS_ANALYSIS");
388 Remove indirection and memory access of known variables.
390 if (!lower_memory_access (cfg
))
394 By replacing indirect access with direct operations, some LDADDR ops become dead. Kill them.
396 if (cfg
->opt
& MONO_OPT_DEADCE
)
397 mono_local_deadce (cfg
);
400 Some variables no longer need to be flagged as indirect, find them.
401 Since indirect vars are converted into global vregs, each pass eliminates only one level of indirection.
402 Most cases only need one pass and some 2.
404 for (i
= 0; i
< 3 && restored_vars
> 0 && recompute_aliased_variables (cfg
, &restored_vars
); ++i
) {
406 A lot of simplification just took place, we recompute local variables and do DCE to
407 really profit from the previous gains
409 mono_handle_global_vregs (cfg
);
410 if (cfg
->opt
& MONO_OPT_DEADCE
)
411 mono_local_deadce (cfg
);
415 if (cfg
->verbose_level
> 2)
416 mono_print_code (cfg
, "AFTER ALIAS_ANALYSIS");
419 #else /* !DISABLE_JIT */
421 MONO_EMPTY_SOURCE_FILE (alias_analysis
);
423 #endif /* !DISABLE_JIT */