[profiler] Remove support for the output=-FILENAME option.
[mono-project.git] / mono / mini / alias-analysis.c
blobc30de719a428d6a5683febbcdf1e00295edde822
1 /**
2 * \file
3 * Implement simple alias analysis for local variables.
5 * Author:
6 * Rodrigo Kumpera (kumpera@gmail.com)
8 * (C) 2013 Xamarin
9 */
11 #include <config.h>
12 #include <stdio.h>
14 #include "mini.h"
15 #include "ir-emit.h"
16 #include "glib.h"
17 #include <mono/utils/mono-compiler.h>
19 #ifndef DISABLE_JIT
21 static gboolean
22 is_int_stack_size (int type)
24 #if SIZEOF_VOID_P == 4
25 return type == STACK_I4 || type == STACK_MP;
26 #else
27 return type == STACK_I4;
28 #endif
31 static gboolean
32 is_long_stack_size (int type)
34 #if SIZEOF_VOID_P == 8
35 return type == STACK_I8 || type == STACK_MP;
36 #else
37 return type == STACK_I8;
38 #endif
42 static gboolean
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);
52 return FALSE;
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));
60 return FALSE;
61 } else {
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++;
69 return TRUE;
72 static gboolean
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);
82 return FALSE;
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));
91 return FALSE;
92 } else {
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++;
100 return TRUE;
103 static gboolean
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)
110 return FALSE;
112 switch (store->opcode) {
113 #if SIZEOF_VOID_P == 4
114 case OP_STORE_MEMBASE_IMM:
115 #endif
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");
119 return FALSE;
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;
126 break;
128 #if SIZEOF_VOID_P == 8
129 case OP_STORE_MEMBASE_IMM:
130 #endif
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");
134 return FALSE;
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;
141 break;
142 default:
143 return FALSE;
145 mono_jit_stats.stores_eliminated++;
146 return TRUE;
149 static gboolean
150 lower_memory_access (MonoCompile *cfg)
152 MonoBasicBlock *bb;
153 MonoInst *ins, *tmp;
154 gboolean needs_dce = FALSE;
155 GHashTable *addr_loads = g_hash_table_new (NULL, NULL);
156 //FIXME optimize
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) {
161 handle_instruction:
162 switch (ins->opcode) {
163 case OP_LDADDR: {
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); }
167 } else {
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); }
171 break;
174 case OP_MOVE:
175 tmp = (MonoInst*)g_hash_table_lookup (addr_loads, GINT_TO_POINTER (ins->sreg1));
177 Forward propagate known aliases
178 ldaddr R10 <- R8
179 mov R11 <- R10
181 if (tmp) {
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); }
184 } else {
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); }
192 break;
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:
205 #endif
206 case OP_LOADR8_MEMBASE:
207 if (ins->inst_offset != 0)
208 continue;
209 tmp = (MonoInst *)g_hash_table_lookup (addr_loads, GINT_TO_POINTER (ins->sreg1));
210 if (tmp) {
211 if (cfg->verbose_level > 2) { printf ("Found candidate load:"); mono_print_ins (ins); }
212 if (lower_load (cfg, ins, tmp)) {
213 needs_dce = TRUE;
214 /* Try to propagate known aliases if an OP_MOVE was inserted */
215 goto handle_instruction;
218 break;
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:
227 #endif
228 case OP_STORER8_MEMBASE_REG:
229 case OP_STOREV_MEMBASE:
230 if (ins->inst_offset != 0)
231 continue;
232 tmp = (MonoInst *)g_hash_table_lookup (addr_loads, GINT_TO_POINTER (ins->dreg));
233 if (tmp) {
234 if (cfg->verbose_level > 2) { printf ("Found candidate store:"); mono_print_ins (ins); }
235 if (lower_store (cfg, ins, tmp)) {
236 needs_dce = TRUE;
237 /* Try to propagate known aliases if an OP_MOVE was inserted */
238 goto handle_instruction;
241 break;
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)
247 continue;
248 tmp = (MonoInst *)g_hash_table_lookup (addr_loads, GINT_TO_POINTER (ins->dreg));
249 if (tmp) {
250 if (cfg->verbose_level > 2) { printf ("Found candidate store-imm:"); mono_print_ins (ins); }
251 needs_dce |= lower_store_imm (cfg, ins, tmp);
253 break;
254 case OP_CHECK_THIS:
255 case OP_NOT_NULL:
256 tmp = (MonoInst *)g_hash_table_lookup (addr_loads, GINT_TO_POINTER (ins->sreg1));
257 if (tmp) {
258 if (cfg->verbose_level > 2) { printf ("Found null check over local: "); mono_print_ins (ins); }
259 NULLIFY_INS (ins);
260 needs_dce = TRUE;
262 break;
266 g_hash_table_destroy (addr_loads);
267 return needs_dce;
270 static gboolean
271 recompute_aliased_variables (MonoCompile *cfg, int *restored_vars)
273 int i;
274 MonoBasicBlock *bb;
275 MonoInst *ins;
276 int kills = 0;
277 int adds = 0;
278 *restored_vars = 0;
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);
286 ++kills;
288 var->flags &= ~MONO_INST_INDIRECT;
291 if (!kills)
292 return FALSE;
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) {
297 MonoInst *var;
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); }
304 ++adds;
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;
314 if (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);
319 return TRUE;
321 return FALSE;
325 FIXME:
326 Don't DCE on the whole CFG, only the BBs that have changed.
328 TODO:
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.
332 void
333 mono_local_alias_analysis (MonoCompile *cfg)
335 int i, restored_vars = 1;
336 if (!cfg->has_indirection)
337 return;
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))
346 goto done;
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);
369 done:
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 */