2 * alias-analysis.c: Implement simple alias analysis for local variables.
5 * Rodrigo Kumpera (kumpera@gmail.com)
16 #include <mono/utils/mono-compiler.h>
21 is_int_stack_size (int type)
23 #if SIZEOF_VOID_P == 4
24 return type == STACK_I4 || type == STACK_MP;
26 return type == STACK_I4;
31 is_long_stack_size (int type)
33 #if SIZEOF_VOID_P == 8
34 return type == STACK_I8 || type == STACK_MP;
36 return type == STACK_I8;
42 lower_load (MonoCompile *cfg, MonoInst *load, MonoInst *ldaddr)
44 MonoInst *var = (MonoInst *)ldaddr->inst_p0;
45 MonoType *type = &var->klass->byval_arg;
46 int replaced_op = mono_type_to_load_membase (cfg, type);
48 if (load->opcode == OP_LOADV_MEMBASE && load->klass != var->klass) {
49 if (cfg->verbose_level > 2)
50 printf ("Incompatible load_vtype classes %s x %s\n", load->klass->name, var->klass->name);
54 if (replaced_op != load->opcode) {
55 if (cfg->verbose_level > 2)
56 printf ("Incompatible load type: expected %s but got %s\n",
57 mono_inst_name (replaced_op),
58 mono_inst_name (load->opcode));
61 if (cfg->verbose_level > 2) { printf ("mem2reg replacing: "); mono_print_ins (load); }
64 load->opcode = mono_type_to_regmove (cfg, type);
65 type_to_eval_stack_type (cfg, type, load);
66 load->sreg1 = var->dreg;
67 mono_jit_stats.loads_eliminated++;
72 lower_store (MonoCompile *cfg, MonoInst *store, MonoInst *ldaddr)
74 MonoInst *var = (MonoInst *)ldaddr->inst_p0;
75 MonoType *type = &var->klass->byval_arg;
76 int replaced_op = mono_type_to_store_membase (cfg, type);
78 if (store->opcode == OP_STOREV_MEMBASE && store->klass != var->klass) {
79 if (cfg->verbose_level > 2)
80 printf ("Incompatible store_vtype classes %s x %s\n", store->klass->name, store->klass->name);
85 if (replaced_op != store->opcode) {
86 if (cfg->verbose_level > 2)
87 printf ("Incompatible store_reg type: expected %s but got %s\n",
88 mono_inst_name (replaced_op),
89 mono_inst_name (store->opcode));
92 if (cfg->verbose_level > 2) { printf ("mem2reg replacing: "); mono_print_ins (store); }
95 store->opcode = mono_type_to_regmove (cfg, type);
96 type_to_eval_stack_type (cfg, type, store);
97 store->dreg = var->dreg;
98 mono_jit_stats.stores_eliminated++;
103 lower_store_imm (MonoCompile *cfg, MonoInst *store, MonoInst *ldaddr)
105 MonoInst *var = (MonoInst *)ldaddr->inst_p0;
106 MonoType *type = &var->klass->byval_arg;
107 int store_op = mono_type_to_store_membase (cfg, type);
108 if (store_op == OP_STOREV_MEMBASE || store_op == OP_STOREX_MEMBASE)
111 switch (store->opcode) {
112 #if SIZEOF_VOID_P == 4
113 case OP_STORE_MEMBASE_IMM:
115 case OP_STOREI4_MEMBASE_IMM:
116 if (!is_int_stack_size (var->type)) {
117 if (cfg->verbose_level > 2) printf ("Incompatible variable of size != 4\n");
120 if (cfg->verbose_level > 2) { printf ("mem2reg replacing: "); mono_print_ins (store); }
121 store->opcode = OP_ICONST;
122 store->type = STACK_I4;
123 store->dreg = var->dreg;
124 store->inst_c0 = store->inst_imm;
127 #if SIZEOF_VOID_P == 8
128 case OP_STORE_MEMBASE_IMM:
130 case OP_STOREI8_MEMBASE_IMM:
131 if (!is_long_stack_size (var->type)) {
132 if (cfg->verbose_level > 2) printf ("Incompatible variable of size != 8\n");
135 if (cfg->verbose_level > 2) { printf ("mem2reg replacing: "); mono_print_ins (store); }
136 store->opcode = OP_I8CONST;
137 store->type = STACK_I8;
138 store->dreg = var->dreg;
139 store->inst_l = store->inst_imm;
144 mono_jit_stats.stores_eliminated++;
149 lower_memory_access (MonoCompile *cfg)
153 gboolean needs_dce = FALSE;
154 GHashTable *addr_loads = g_hash_table_new (NULL, NULL);
156 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
157 g_hash_table_remove_all (addr_loads);
159 for (ins = bb->code; ins; ins = ins->next) {
161 switch (ins->opcode) {
163 MonoInst *var = (MonoInst*)ins->inst_p0;
164 if (var->flags & MONO_INST_VOLATILE) {
165 if (cfg->verbose_level > 2) { printf ("Found address to volatile var, can't take it: "); mono_print_ins (ins); }
167 g_hash_table_insert (addr_loads, GINT_TO_POINTER (ins->dreg), ins);
168 if (cfg->verbose_level > 2) { printf ("New address: "); mono_print_ins (ins); }
174 tmp = (MonoInst*)g_hash_table_lookup (addr_loads, GINT_TO_POINTER (ins->sreg1));
176 Forward propagate known aliases
181 g_hash_table_insert (addr_loads, GINT_TO_POINTER (ins->dreg), tmp);
182 if (cfg->verbose_level > 2) { printf ("New alias: "); mono_print_ins (ins); }
185 Source value is not a know address, kill the variable.
187 if (g_hash_table_remove (addr_loads, GINT_TO_POINTER (ins->dreg))) {
188 if (cfg->verbose_level > 2) { printf ("Killed alias: "); mono_print_ins (ins); }
193 case OP_LOADV_MEMBASE:
194 case OP_LOAD_MEMBASE:
195 case OP_LOADU1_MEMBASE:
196 case OP_LOADI2_MEMBASE:
197 case OP_LOADU2_MEMBASE:
198 case OP_LOADI4_MEMBASE:
199 case OP_LOADU4_MEMBASE:
200 case OP_LOADI1_MEMBASE:
201 case OP_LOADI8_MEMBASE:
202 #ifndef MONO_ARCH_SOFT_FLOAT_FALLBACK
203 case OP_LOADR4_MEMBASE:
205 case OP_LOADR8_MEMBASE:
206 if (ins->inst_offset != 0)
208 tmp = (MonoInst *)g_hash_table_lookup (addr_loads, GINT_TO_POINTER (ins->sreg1));
210 if (cfg->verbose_level > 2) { printf ("Found candidate load:"); mono_print_ins (ins); }
211 if (lower_load (cfg, ins, tmp)) {
213 /* Try to propagate known aliases if an OP_MOVE was inserted */
214 goto handle_instruction;
219 case OP_STORE_MEMBASE_REG:
220 case OP_STOREI1_MEMBASE_REG:
221 case OP_STOREI2_MEMBASE_REG:
222 case OP_STOREI4_MEMBASE_REG:
223 case OP_STOREI8_MEMBASE_REG:
224 #ifndef MONO_ARCH_SOFT_FLOAT_FALLBACK
225 case OP_STORER4_MEMBASE_REG:
227 case OP_STORER8_MEMBASE_REG:
228 case OP_STOREV_MEMBASE:
229 if (ins->inst_offset != 0)
231 tmp = (MonoInst *)g_hash_table_lookup (addr_loads, GINT_TO_POINTER (ins->dreg));
233 if (cfg->verbose_level > 2) { printf ("Found candidate store:"); mono_print_ins (ins); }
234 if (lower_store (cfg, ins, tmp)) {
236 /* Try to propagate known aliases if an OP_MOVE was inserted */
237 goto handle_instruction;
241 //FIXME missing storei1_membase_imm and storei2_membase_imm
242 case OP_STORE_MEMBASE_IMM:
243 case OP_STOREI4_MEMBASE_IMM:
244 case OP_STOREI8_MEMBASE_IMM:
245 if (ins->inst_offset != 0)
247 tmp = (MonoInst *)g_hash_table_lookup (addr_loads, GINT_TO_POINTER (ins->dreg));
249 if (cfg->verbose_level > 2) { printf ("Found candidate store-imm:"); mono_print_ins (ins); }
250 needs_dce |= lower_store_imm (cfg, ins, tmp);
255 tmp = (MonoInst *)g_hash_table_lookup (addr_loads, GINT_TO_POINTER (ins->sreg1));
257 if (cfg->verbose_level > 2) { printf ("Found null check over local: "); mono_print_ins (ins); }
265 g_hash_table_destroy (addr_loads);
270 recompute_aliased_variables (MonoCompile *cfg, int *restored_vars)
279 for (i = 0; i < cfg->num_varinfo; i++) {
280 MonoInst *var = cfg->varinfo [i];
281 if (var->flags & MONO_INST_INDIRECT) {
282 if (cfg->verbose_level > 2) {
283 printf ("Killing :"); mono_print_ins (var);
287 var->flags &= ~MONO_INST_INDIRECT;
293 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
294 for (ins = bb->code; ins; ins = ins->next) {
295 if (ins->opcode == OP_LDADDR) {
298 if (cfg->verbose_level > 2) { printf ("Found op :"); mono_print_ins (ins); }
300 var = (MonoInst*)ins->inst_p0;
301 if (!(var->flags & MONO_INST_INDIRECT)) {
302 if (cfg->verbose_level > 1) { printf ("Restoring :"); mono_print_ins (var); }
305 var->flags |= MONO_INST_INDIRECT;
309 *restored_vars = adds;
311 mono_jit_stats.alias_found += kills;
312 mono_jit_stats.alias_removed += kills - adds;
314 if (cfg->verbose_level > 2) {
315 printf ("Method: %s\n", mono_method_full_name (cfg->method, 1));
316 printf ("Kills %d Adds %d\n", kills, adds);
325 Don't DCE on the whole CFG, only the BBs that have changed.
328 SRVT of small types can fix cases of mismatch for fields of a different type than the component.
329 Handle aliasing of byrefs in call conventions.
332 mono_local_alias_analysis (MonoCompile *cfg)
334 int i, restored_vars = 1;
335 if (!cfg->has_indirection)
338 if (cfg->verbose_level > 2)
339 mono_print_code (cfg, "BEFORE ALIAS_ANALYSIS");
342 Remove indirection and memory access of known variables.
344 if (!lower_memory_access (cfg))
348 By replacing indirect access with direct operations, some LDADDR ops become dead. Kill them.
350 if (cfg->opt & MONO_OPT_DEADCE)
351 mono_local_deadce (cfg);
354 Some variables no longer need to be flagged as indirect, find them.
355 Since indirect vars are converted into global vregs, each pass eliminates only one level of indirection.
356 Most cases only need one pass and some 2.
358 for (i = 0; i < 3 && restored_vars > 0 && recompute_aliased_variables (cfg, &restored_vars); ++i) {
360 A lot of simplification just took place, we recompute local variables and do DCE to
361 really profit from the previous gains
363 mono_handle_global_vregs (cfg);
364 if (cfg->opt & MONO_OPT_DEADCE)
365 mono_local_deadce (cfg);
369 if (cfg->verbose_level > 2)
370 mono_print_code (cfg, "AFTER ALIAS_ANALYSIS");
373 #else /* !DISABLE_JIT */
375 MONO_EMPTY_SOURCE_FILE (alias_analysis);
377 #endif /* !DISABLE_JIT */