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 SIZEOF_VOID_P == 4
25 return type
== STACK_I4
|| type
== STACK_MP
;
27 return type
== STACK_I4
;
32 is_long_stack_size (int type
)
34 #if SIZEOF_VOID_P == 8
35 return type
== STACK_I8
|| type
== STACK_MP
;
37 return type
== STACK_I8
;
43 lower_load (MonoCompile
*cfg
, MonoInst
*load
, MonoInst
*ldaddr
)
45 MonoInst
*var
= (MonoInst
*)ldaddr
->inst_p0
;
46 MonoType
*type
= &var
->klass
->byval_arg
;
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", load
->klass
->name
, var
->klass
->name
);
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 type_to_eval_stack_type (cfg
, type
, load
);
67 load
->sreg1
= var
->dreg
;
68 mono_jit_stats
.loads_eliminated
++;
73 lower_store (MonoCompile
*cfg
, MonoInst
*store
, MonoInst
*ldaddr
)
75 MonoInst
*var
= (MonoInst
*)ldaddr
->inst_p0
;
76 MonoType
*type
= &var
->klass
->byval_arg
;
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", store
->klass
->name
, store
->klass
->name
);
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 store
->opcode
= mono_type_to_regmove (cfg
, type
);
97 type_to_eval_stack_type (cfg
, type
, store
);
98 store
->dreg
= var
->dreg
;
99 mono_jit_stats
.stores_eliminated
++;
104 lower_store_imm (MonoCompile
*cfg
, MonoInst
*store
, MonoInst
*ldaddr
)
106 MonoInst
*var
= (MonoInst
*)ldaddr
->inst_p0
;
107 MonoType
*type
= &var
->klass
->byval_arg
;
108 int store_op
= mono_type_to_store_membase (cfg
, type
);
109 if (store_op
== OP_STOREV_MEMBASE
|| store_op
== OP_STOREX_MEMBASE
)
112 switch (store
->opcode
) {
113 #if SIZEOF_VOID_P == 4
114 case OP_STORE_MEMBASE_IMM
:
116 case OP_STOREI4_MEMBASE_IMM
:
117 if (!is_int_stack_size (var
->type
)) {
118 if (cfg
->verbose_level
> 2) printf ("Incompatible variable of size != 4\n");
121 if (cfg
->verbose_level
> 2) { printf ("mem2reg replacing: "); mono_print_ins (store
); }
122 store
->opcode
= OP_ICONST
;
123 store
->type
= STACK_I4
;
124 store
->dreg
= var
->dreg
;
125 store
->inst_c0
= store
->inst_imm
;
128 #if SIZEOF_VOID_P == 8
129 case OP_STORE_MEMBASE_IMM
:
131 case OP_STOREI8_MEMBASE_IMM
:
132 if (!is_long_stack_size (var
->type
)) {
133 if (cfg
->verbose_level
> 2) printf ("Incompatible variable of size != 8\n");
136 if (cfg
->verbose_level
> 2) { printf ("mem2reg replacing: "); mono_print_ins (store
); }
137 store
->opcode
= OP_I8CONST
;
138 store
->type
= STACK_I8
;
139 store
->dreg
= var
->dreg
;
140 store
->inst_l
= store
->inst_imm
;
145 mono_jit_stats
.stores_eliminated
++;
150 lower_memory_access (MonoCompile
*cfg
)
154 gboolean needs_dce
= FALSE
;
155 GHashTable
*addr_loads
= g_hash_table_new (NULL
, NULL
);
157 for (bb
= cfg
->bb_entry
; bb
; bb
= bb
->next_bb
) {
158 g_hash_table_remove_all (addr_loads
);
160 for (ins
= bb
->code
; ins
; ins
= ins
->next
) {
162 switch (ins
->opcode
) {
164 MonoInst
*var
= (MonoInst
*)ins
->inst_p0
;
165 if (var
->flags
& MONO_INST_VOLATILE
) {
166 if (cfg
->verbose_level
> 2) { printf ("Found address to volatile var, can't take it: "); mono_print_ins (ins
); }
168 g_hash_table_insert (addr_loads
, GINT_TO_POINTER (ins
->dreg
), ins
);
169 if (cfg
->verbose_level
> 2) { printf ("New address: "); mono_print_ins (ins
); }
175 tmp
= (MonoInst
*)g_hash_table_lookup (addr_loads
, GINT_TO_POINTER (ins
->sreg1
));
177 Forward propagate known aliases
182 g_hash_table_insert (addr_loads
, GINT_TO_POINTER (ins
->dreg
), tmp
);
183 if (cfg
->verbose_level
> 2) { printf ("New alias: "); mono_print_ins (ins
); }
186 Source value is not a know address, kill the variable.
188 if (g_hash_table_remove (addr_loads
, GINT_TO_POINTER (ins
->dreg
))) {
189 if (cfg
->verbose_level
> 2) { printf ("Killed alias: "); mono_print_ins (ins
); }
194 case OP_LOADV_MEMBASE
:
195 case OP_LOAD_MEMBASE
:
196 case OP_LOADU1_MEMBASE
:
197 case OP_LOADI2_MEMBASE
:
198 case OP_LOADU2_MEMBASE
:
199 case OP_LOADI4_MEMBASE
:
200 case OP_LOADU4_MEMBASE
:
201 case OP_LOADI1_MEMBASE
:
202 case OP_LOADI8_MEMBASE
:
203 #ifndef MONO_ARCH_SOFT_FLOAT_FALLBACK
204 case OP_LOADR4_MEMBASE
:
206 case OP_LOADR8_MEMBASE
:
207 if (ins
->inst_offset
!= 0)
209 tmp
= (MonoInst
*)g_hash_table_lookup (addr_loads
, GINT_TO_POINTER (ins
->sreg1
));
211 if (cfg
->verbose_level
> 2) { printf ("Found candidate load:"); mono_print_ins (ins
); }
212 if (lower_load (cfg
, ins
, tmp
)) {
214 /* Try to propagate known aliases if an OP_MOVE was inserted */
215 goto handle_instruction
;
220 case OP_STORE_MEMBASE_REG
:
221 case OP_STOREI1_MEMBASE_REG
:
222 case OP_STOREI2_MEMBASE_REG
:
223 case OP_STOREI4_MEMBASE_REG
:
224 case OP_STOREI8_MEMBASE_REG
:
225 #ifndef MONO_ARCH_SOFT_FLOAT_FALLBACK
226 case OP_STORER4_MEMBASE_REG
:
228 case OP_STORER8_MEMBASE_REG
:
229 case OP_STOREV_MEMBASE
:
230 if (ins
->inst_offset
!= 0)
232 tmp
= (MonoInst
*)g_hash_table_lookup (addr_loads
, GINT_TO_POINTER (ins
->dreg
));
234 if (cfg
->verbose_level
> 2) { printf ("Found candidate store:"); mono_print_ins (ins
); }
235 if (lower_store (cfg
, ins
, tmp
)) {
237 /* Try to propagate known aliases if an OP_MOVE was inserted */
238 goto handle_instruction
;
242 //FIXME missing storei1_membase_imm and storei2_membase_imm
243 case OP_STORE_MEMBASE_IMM
:
244 case OP_STOREI4_MEMBASE_IMM
:
245 case OP_STOREI8_MEMBASE_IMM
:
246 if (ins
->inst_offset
!= 0)
248 tmp
= (MonoInst
*)g_hash_table_lookup (addr_loads
, GINT_TO_POINTER (ins
->dreg
));
250 if (cfg
->verbose_level
> 2) { printf ("Found candidate store-imm:"); mono_print_ins (ins
); }
251 needs_dce
|= lower_store_imm (cfg
, ins
, tmp
);
256 tmp
= (MonoInst
*)g_hash_table_lookup (addr_loads
, GINT_TO_POINTER (ins
->sreg1
));
258 if (cfg
->verbose_level
> 2) { printf ("Found null check over local: "); mono_print_ins (ins
); }
266 g_hash_table_destroy (addr_loads
);
271 recompute_aliased_variables (MonoCompile
*cfg
, int *restored_vars
)
280 for (i
= 0; i
< cfg
->num_varinfo
; i
++) {
281 MonoInst
*var
= cfg
->varinfo
[i
];
282 if (var
->flags
& MONO_INST_INDIRECT
) {
283 if (cfg
->verbose_level
> 2) {
284 printf ("Killing :"); mono_print_ins (var
);
288 var
->flags
&= ~MONO_INST_INDIRECT
;
294 for (bb
= cfg
->bb_entry
; bb
; bb
= bb
->next_bb
) {
295 for (ins
= bb
->code
; ins
; ins
= ins
->next
) {
296 if (ins
->opcode
== OP_LDADDR
) {
299 if (cfg
->verbose_level
> 2) { printf ("Found op :"); mono_print_ins (ins
); }
301 var
= (MonoInst
*)ins
->inst_p0
;
302 if (!(var
->flags
& MONO_INST_INDIRECT
)) {
303 if (cfg
->verbose_level
> 1) { printf ("Restoring :"); mono_print_ins (var
); }
306 var
->flags
|= MONO_INST_INDIRECT
;
310 *restored_vars
= adds
;
312 mono_jit_stats
.alias_found
+= kills
;
313 mono_jit_stats
.alias_removed
+= kills
- adds
;
315 if (cfg
->verbose_level
> 2) {
316 printf ("Method: %s\n", mono_method_full_name (cfg
->method
, 1));
317 printf ("Kills %d Adds %d\n", kills
, adds
);
326 Don't DCE on the whole CFG, only the BBs that have changed.
329 SRVT of small types can fix cases of mismatch for fields of a different type than the component.
330 Handle aliasing of byrefs in call conventions.
333 mono_local_alias_analysis (MonoCompile
*cfg
)
335 int i
, restored_vars
= 1;
336 if (!cfg
->has_indirection
)
339 if (cfg
->verbose_level
> 2)
340 mono_print_code (cfg
, "BEFORE ALIAS_ANALYSIS");
343 Remove indirection and memory access of known variables.
345 if (!lower_memory_access (cfg
))
349 By replacing indirect access with direct operations, some LDADDR ops become dead. Kill them.
351 if (cfg
->opt
& MONO_OPT_DEADCE
)
352 mono_local_deadce (cfg
);
355 Some variables no longer need to be flagged as indirect, find them.
356 Since indirect vars are converted into global vregs, each pass eliminates only one level of indirection.
357 Most cases only need one pass and some 2.
359 for (i
= 0; i
< 3 && restored_vars
> 0 && recompute_aliased_variables (cfg
, &restored_vars
); ++i
) {
361 A lot of simplification just took place, we recompute local variables and do DCE to
362 really profit from the previous gains
364 mono_handle_global_vregs (cfg
);
365 if (cfg
->opt
& MONO_OPT_DEADCE
)
366 mono_local_deadce (cfg
);
370 if (cfg
->verbose_level
> 2)
371 mono_print_code (cfg
, "AFTER ALIAS_ANALYSIS");
374 #else /* !DISABLE_JIT */
376 MONO_EMPTY_SOURCE_FILE (alias_analysis
);
378 #endif /* !DISABLE_JIT */