6 * Dietmar Maurer (dietmar@ximian.com)
8 * (C) 2002 Ximian, Inc.
12 #include <mono/metadata/debug-helpers.h>
13 #include <mono/utils/mono-compiler.h>
17 static void mono_linear_scan2 (MonoCompile
*cfg
, GList
*vars
, GList
*regs
, regmask_t
*used_mask
);
20 mono_varlist_insert_sorted (MonoCompile
*cfg
, GList
*list
, MonoMethodVar
*mv
, int sort_type
)
25 return g_list_prepend (NULL
, mv
);
27 for (l
= list
; l
; l
= l
->next
) {
28 MonoMethodVar
*v1
= (MonoMethodVar
*)l
->data
;
31 if (mv
->spill_costs
>= v1
->spill_costs
) {
32 list
= g_list_insert_before (list
, l
, mv
);
35 } else if (sort_type
== 1) {
36 if (mv
->range
.last_use
.abs_pos
<= v1
->range
.last_use
.abs_pos
) {
37 list
= g_list_insert_before (list
, l
, mv
);
41 if (mv
->range
.first_use
.abs_pos
<= v1
->range
.first_use
.abs_pos
) {
42 list
= g_list_insert_before (list
, l
, mv
);
48 list
= g_list_append (list
, mv
);
54 compare_by_first_use_func (gconstpointer a
, gconstpointer b
)
56 MonoMethodVar
*v1
= (MonoMethodVar
*)a
;
57 MonoMethodVar
*v2
= (MonoMethodVar
*)b
;
59 return v1
->range
.first_use
.abs_pos
- v2
->range
.first_use
.abs_pos
;
63 mono_varlist_sort (MonoCompile
*cfg
, GList
*list
, int sort_type
)
66 return g_list_sort (list
, compare_by_first_use_func
);
68 g_assert_not_reached ();
73 // #define DEBUG_LSCAN
76 mono_linear_scan (MonoCompile
*cfg
, GList
*vars
, GList
*regs
, regmask_t
*used_mask
)
78 GList
*l
, *a
, *active
= NULL
;
79 MonoMethodVar
*vmv
, *amv
;
80 int max_regs
, n_regvars
;
81 int gains
[sizeof (regmask_t
) * 8];
82 regmask_t used_regs
= 0;
85 if (!cfg
->disable_reuse_registers
&& vars
&& (((MonoMethodVar
*)vars
->data
)->interval
!= NULL
)) {
86 mono_linear_scan2 (cfg
, vars
, regs
, used_mask
);
95 printf ("Linears scan for %s\n", mono_method_full_name (cfg
->method
, TRUE
));
99 for (l
= vars
; l
; l
= l
->next
) {
101 printf ("VAR %d %08x %08x C%d\n", vmv
->idx
, vmv
->range
.first_use
.abs_pos
,
102 vmv
->range
.last_use
.abs_pos
, vmv
->spill_costs
);
105 max_regs
= g_list_length (regs
);
107 for (l
= regs
; l
; l
= l
->next
) {
108 int regnum
= GPOINTER_TO_INT (l
->data
);
109 g_assert (regnum
< G_N_ELEMENTS (gains
));
114 for (l
= vars
; l
; l
= l
->next
) {
115 vmv
= (MonoMethodVar
*)l
->data
;
118 printf ("START %2d %08x %08x\n", vmv
->idx
, vmv
->range
.first_use
.abs_pos
,
119 vmv
->range
.last_use
.abs_pos
);
121 /* expire old intervals in active */
122 if (!cfg
->disable_reuse_registers
) {
124 amv
= (MonoMethodVar
*)active
->data
;
126 if (amv
->range
.last_use
.abs_pos
> vmv
->range
.first_use
.abs_pos
)
130 printf ("EXPIR %2d %08x %08x C%d R%d\n", amv
->idx
, amv
->range
.first_use
.abs_pos
,
131 amv
->range
.last_use
.abs_pos
, amv
->spill_costs
, amv
->reg
);
133 active
= g_list_delete_link (active
, active
);
134 regs
= g_list_prepend (regs
, GINT_TO_POINTER (amv
->reg
));
135 gains
[amv
->reg
] += amv
->spill_costs
;
139 if (active
&& g_list_length (active
) == max_regs
) {
142 a
= g_list_nth (active
, max_regs
- 1);
143 amv
= (MonoMethodVar
*)a
->data
;
145 if ((cost_driven
&& amv
->spill_costs
< vmv
->spill_costs
) ||
146 (!cost_driven
&& amv
->range
.last_use
.abs_pos
> vmv
->range
.last_use
.abs_pos
)) {
149 active
= g_list_delete_link (active
, a
);
152 active
= mono_varlist_insert_sorted (cfg
, active
, vmv
, 2);
154 active
= mono_varlist_insert_sorted (cfg
, active
, vmv
, 1);
157 printf ("SPILL0 %2d %08x %08x C%d\n", amv
->idx
,
158 amv
->range
.first_use
.abs_pos
, amv
->range
.last_use
.abs_pos
,
163 printf ("SPILL1 %2d %08x %08x C%d\n", vmv
->idx
,
164 vmv
->range
.first_use
.abs_pos
, vmv
->range
.last_use
.abs_pos
,
170 /* assign register */
174 vmv
->reg
= GPOINTER_TO_INT (regs
->data
);
176 used_regs
|= 1LL << vmv
->reg
;
178 regs
= g_list_delete_link (regs
, regs
);
181 printf ("ADD %2d %08x %08x C%d R%d\n", vmv
->idx
,
182 vmv
->range
.first_use
.abs_pos
, vmv
->range
.last_use
.abs_pos
,
183 vmv
->spill_costs
, vmv
->reg
);
185 active
= mono_varlist_insert_sorted (cfg
, active
, vmv
, TRUE
);
190 for (a
= active
; a
; a
= a
->next
) {
191 amv
= (MonoMethodVar
*)a
->data
;
192 printf ("ACT %2d %08x %08x C%d R%d\n", amv
->idx
, amv
->range
.first_use
.abs_pos
,
193 amv
->range
.last_use
.abs_pos
, amv
->spill_costs
, amv
->reg
);
199 for (a
= active
; a
; a
= a
->next
) {
200 amv
= (MonoMethodVar
*)a
->data
;
201 gains
[amv
->reg
] += amv
->spill_costs
;
205 for (l
= vars
; l
; l
= l
->next
) {
206 vmv
= (MonoMethodVar
*)l
->data
;
209 if ((gains
[vmv
->reg
] > mono_arch_regalloc_cost (cfg
, vmv
)) && (cfg
->varinfo
[vmv
->idx
]->opcode
!= OP_REGVAR
)) {
210 if (cfg
->verbose_level
> 2) {
211 printf ("ALLOCATED R%d(%d) TO HREG %d COST %d\n", cfg
->varinfo
[vmv
->idx
]->dreg
, vmv
->idx
, vmv
->reg
, vmv
->spill_costs
);
213 cfg
->varinfo
[vmv
->idx
]->opcode
= OP_REGVAR
;
214 cfg
->varinfo
[vmv
->idx
]->dreg
= vmv
->reg
;
217 if (cfg
->verbose_level
> 2)
218 printf ("COSTLY: R%d C%d C%d %s\n", vmv
->idx
, vmv
->spill_costs
, mono_arch_regalloc_cost (cfg
, vmv
), mono_arch_regname (vmv
->reg
));
223 if (vmv
->reg
== -1) {
224 if (cfg
->verbose_level
> 2)
225 printf ("NOT REGVAR: %d\n", vmv
->idx
);
229 cfg
->stat_n_regvars
= n_regvars
;
231 /* Compute used regs */
233 for (l
= vars
; l
; l
= l
->next
) {
234 vmv
= (MonoMethodVar
*)l
->data
;
237 used_regs
|= 1LL << vmv
->reg
;
240 *used_mask
|= used_regs
;
243 if (cfg
->verbose_level
> 2)
244 printf ("EXIT: final used mask: %08x\n", *used_mask
);
248 g_list_free (active
);
253 compare_by_interval_start_pos_func (gconstpointer a
, gconstpointer b
)
255 MonoMethodVar
*v1
= (MonoMethodVar
*)a
;
256 MonoMethodVar
*v2
= (MonoMethodVar
*)b
;
260 else if (v1
->interval
->range
&& v2
->interval
->range
)
261 return v1
->interval
->range
->from
- v2
->interval
->range
->from
;
262 else if (v1
->interval
->range
)
269 #define LSCAN_DEBUG(a) do { a; } while (0)
271 #define LSCAN_DEBUG(a)
274 /* FIXME: This is x86 only */
276 regalloc_cost (MonoCompile
*cfg
, MonoMethodVar
*vmv
)
278 MonoInst
*ins
= cfg
->varinfo
[vmv
->idx
];
280 /* Load if it is an argument */
281 return (ins
->opcode
== OP_ARG
) ? 1 : 0;
285 mono_linear_scan2 (MonoCompile
*cfg
, GList
*vars
, GList
*regs
, regmask_t
*used_mask
)
287 GList
*unhandled
, *active
, *inactive
, *l
;
289 gint32 free_pos
[sizeof (regmask_t
) * 8];
290 gint32 gains
[sizeof (regmask_t
) * 8];
291 regmask_t used_regs
= 0;
292 int n_regs
, n_regvars
, i
;
294 for (l
= vars
; l
; l
= l
->next
) {
295 vmv
= (MonoMethodVar
*)l
->data
;
296 LSCAN_DEBUG (printf ("VAR R%d %08x %08x C%d\n", cfg
->varinfo
[vmv
->idx
]->dreg
, vmv
->range
.first_use
.abs_pos
,
297 vmv
->range
.last_use
.abs_pos
, vmv
->spill_costs
));
300 LSCAN_DEBUG (printf ("Linear Scan 2 for %s:\n", mono_method_full_name (cfg
->method
, TRUE
)));
302 n_regs
= g_list_length (regs
);
303 memset (gains
, 0, n_regs
* sizeof (gint32
));
304 unhandled
= g_list_sort (g_list_copy (vars
), compare_by_interval_start_pos_func
);
309 MonoMethodVar
*current
= (MonoMethodVar
*)unhandled
->data
;
310 int pos
, reg
, max_free_pos
;
313 unhandled
= g_list_delete_link (unhandled
, unhandled
);
315 LSCAN_DEBUG (printf ("Processing R%d: ", cfg
->varinfo
[current
->idx
]->dreg
));
316 LSCAN_DEBUG (mono_linterval_print (current
->interval
));
317 LSCAN_DEBUG (printf ("\n"));
319 if (!current
->interval
->range
)
322 pos
= current
->interval
->range
->from
;
324 /* Check for intervals in active which expired or inactive */
326 /* FIXME: Optimize this */
329 for (l
= active
; l
!= NULL
; l
= l
->next
) {
330 MonoMethodVar
*v
= (MonoMethodVar
*)l
->data
;
332 if (v
->interval
->last_range
->to
< pos
) {
333 active
= g_list_delete_link (active
, l
);
334 LSCAN_DEBUG (printf ("Interval R%d has expired\n", cfg
->varinfo
[v
->idx
]->dreg
));
338 else if (!mono_linterval_covers (v
->interval
, pos
)) {
339 inactive
= g_list_append (inactive
, v
);
340 active
= g_list_delete_link (active
, l
);
341 LSCAN_DEBUG (printf ("Interval R%d became inactive\n", cfg
->varinfo
[v
->idx
]->dreg
));
348 /* Check for intervals in inactive which expired or active */
350 /* FIXME: Optimize this */
353 for (l
= inactive
; l
!= NULL
; l
= l
->next
) {
354 MonoMethodVar
*v
= (MonoMethodVar
*)l
->data
;
356 if (v
->interval
->last_range
->to
< pos
) {
357 inactive
= g_list_delete_link (inactive
, l
);
358 LSCAN_DEBUG (printf ("\tInterval R%d has expired\n", cfg
->varinfo
[v
->idx
]->dreg
));
362 else if (mono_linterval_covers (v
->interval
, pos
)) {
363 active
= g_list_append (active
, v
);
364 inactive
= g_list_delete_link (inactive
, l
);
365 LSCAN_DEBUG (printf ("\tInterval R%d became active\n", cfg
->varinfo
[v
->idx
]->dreg
));
372 /* Find a register for the current interval */
373 for (i
= 0; i
< n_regs
; ++i
)
374 free_pos
[i
] = ((gint32
)0x7fffffff);
376 for (l
= active
; l
!= NULL
; l
= l
->next
) {
377 MonoMethodVar
*v
= (MonoMethodVar
*)l
->data
;
380 free_pos
[v
->reg
] = 0;
381 LSCAN_DEBUG (printf ("\threg %d is busy (cost %d)\n", v
->reg
, v
->spill_costs
));
385 for (l
= inactive
; l
!= NULL
; l
= l
->next
) {
386 MonoMethodVar
*v
= (MonoMethodVar
*)l
->data
;
387 gint32 intersect_pos
;
390 intersect_pos
= mono_linterval_get_intersect_pos (current
->interval
, v
->interval
);
391 if (intersect_pos
!= -1) {
392 free_pos
[v
->reg
] = intersect_pos
;
393 LSCAN_DEBUG (printf ("\threg %d becomes free at %d\n", v
->reg
, intersect_pos
));
400 for (i
= 0; i
< n_regs
; ++i
)
401 if (free_pos
[i
] > max_free_pos
) {
403 max_free_pos
= free_pos
[i
];
406 g_assert (reg
!= -1);
408 if (free_pos
[reg
] >= current
->interval
->last_range
->to
) {
409 /* Register available for whole interval */
411 LSCAN_DEBUG (printf ("\tAssigned hreg %d to R%d\n", reg
, cfg
->varinfo
[current
->idx
]->dreg
));
413 active
= g_list_append (active
, current
);
414 gains
[current
->reg
] += current
->spill_costs
;
418 * free_pos [reg] > 0 means there is a register available for parts
419 * of the interval, so splitting it is possible. This is not yet
420 * supported, so we spill in this case too.
423 /* Spill an interval */
425 /* FIXME: Optimize the selection of the interval */
428 GList
*min_spill_pos
;
431 * This favors registers with big spill costs, thus larger liveness ranges,
432 * thus actually leading to worse code size.
434 guint32 min_spill_value
= G_MAXINT32
;
436 for (l
= active
; l
!= NULL
; l
= l
->next
) {
437 vmv
= (MonoMethodVar
*)l
->data
;
439 if (vmv
->spill_costs
< min_spill_value
) {
441 min_spill_value
= vmv
->spill_costs
;
445 /* Spill either the first active or the current interval */
446 min_spill_pos
= active
;
448 vmv
= (MonoMethodVar
*)min_spill_pos
->data
;
449 if (vmv
->spill_costs
< current
->spill_costs
) {
450 // if (vmv->interval->last_range->to < current->interval->last_range->to) {
451 gains
[vmv
->reg
] -= vmv
->spill_costs
;
453 LSCAN_DEBUG (printf ("\tSpilled R%d\n", cfg
->varinfo
[vmv
->idx
]->dreg
));
454 active
= g_list_delete_link (active
, min_spill_pos
);
457 LSCAN_DEBUG (printf ("\tSpilled current (cost %d)\n", current
->spill_costs
));
460 LSCAN_DEBUG (printf ("\tSpilled current\n"));
464 /* Decrease the gains by the cost of saving+restoring the register */
465 for (i
= 0; i
< n_regs
; ++i
) {
467 /* FIXME: This is x86 only */
468 gains
[i
] -= cfg
->method
->save_lmf
? 1 : 2;
474 /* Do the actual register assignment */
476 for (l
= vars
; l
; l
= l
->next
) {
477 vmv
= (MonoMethodVar
*)l
->data
;
480 int reg_index
= vmv
->reg
;
482 /* During allocation, vmv->reg is an index into the regs list */
483 vmv
->reg
= GPOINTER_TO_INT (g_list_nth_data (regs
, vmv
->reg
));
485 if ((gains
[reg_index
] > regalloc_cost (cfg
, vmv
)) && (cfg
->varinfo
[vmv
->idx
]->opcode
!= OP_REGVAR
)) {
486 if (cfg
->verbose_level
> 2)
487 printf ("REGVAR R%d G%d C%d %s\n", cfg
->varinfo
[vmv
->idx
]->dreg
, gains
[reg_index
], regalloc_cost (cfg
, vmv
), mono_arch_regname (vmv
->reg
));
488 cfg
->varinfo
[vmv
->idx
]->opcode
= OP_REGVAR
;
489 cfg
->varinfo
[vmv
->idx
]->dreg
= vmv
->reg
;
493 if (cfg
->verbose_level
> 2)
494 printf ("COSTLY: %s R%d G%d C%d %s\n", mono_method_full_name (cfg
->method
, TRUE
), cfg
->varinfo
[vmv
->idx
]->dreg
, gains
[reg_index
], regalloc_cost (cfg
, vmv
), mono_arch_regname (vmv
->reg
));
500 cfg
->stat_n_regvars
= n_regvars
;
502 /* Compute used regs */
504 for (l
= vars
; l
; l
= l
->next
) {
505 vmv
= (MonoMethodVar
*)l
->data
;
508 used_regs
|= 1LL << vmv
->reg
;
511 *used_mask
|= used_regs
;
513 g_list_free (active
);
514 g_list_free (inactive
);
517 #else /* !DISABLE_JIT */
519 MONO_EMPTY_SOURCE_FILE (linear_scan
);
521 #endif /* !DISABLE_JIT */