Lecture 7
Top-down Allocator
The idea:
- Machine has k physical registers
- Keep the “busiest” values in an assigned register
- Use the feasible (reserved) set, F, for the rest
- F is the minimal set of registers needed to execute any instruction with all operands in memory
- Move values with no assigned register from/to memory by adding LOADs and STOREs (SPILL CODE)
Basic algorithm (not graph coloring!)
- Rank values by number of occurrences (or some other metric)
- Allocate first k to F values to registers
- Rewrite code (with spill code) to reflect these choices
Example
Take the following ILOC code with the live ranges (on exit) for each virtual register
loadI 1028 => r1 // r1
load r1 => r2 // r1 r2
mult r1,r2 => r3 // r1 r2 r3
loadI 5 => r4 // r1 r2 r3 r4 -- MAXLIVE = 4
sub r4,r2 => r5 // r1 r3 r5
loadI 8 => r6 // r1 r3 r5 r6 -- MAXLIVE = 4
mult r5,r6 => r7 // r1 r3 r7
sub r7,r3 => r8 // r1 r8
store r8 => r1 //-
3 physical registers to allocate: ra, rb, rc
-
1 selected register: f1 (feasible set)
- Note: ILOC needs larger F set
- k = 4, F = 1, (k-F) = 3
-
Consider statements with MAXLIVE > (k-F) (basic algorithm)
-
Spill heuristic
-
- Number of occurrences of virtual register
-
- Length of live range (tie breaker)
-
| Virtual Register | Number of occurrences | Length of live range |
|---|---|---|
| r1 | 4 | 8 |
| r2 | 3 | 3 |
| r3 | 2 | 5 |
| r4 | 2 | 1 |
| r5 | 2 | 2 |
| r6,r7,r8 | 2 | 1 |
In our case, we will spill r3 (meaning our MAXLIVE <= k)
loadI 1028 => r1 // r1
load r1 => r2 // r1 r2
mult r1,r2 => f1 // r1 r2
storeAI f1 => r0, @r3 // ** spill code
loadI 5 => r4 // r1 r2 r4 -- MAXLIVE = 3
sub r4,r2 => r5 // r1 r5
loadI 8 => r6 // r1 r5 r6 -- MAXLIVE = 3
mult r5,r6 => r7 // r1 r7
loadAI r0,@r3 => f1 // ** spill code
sub r7,r3 => r8 // r1 r8
store r8 => r1 //Spill Code
- A virtual register is spilled by using only registers from the feasible set (F), not the allocated set (k-F) = {ra, rb, ..}
- How to insert spill code, with F = {f1, f2, ..}?
- For the definition of the spilled value r (assignment of the value to virtual register r), use a feasible register f as the target register; assign fixed memory location for spilled value (spill offset @r), and store feasible register value to the address offset @r
add ra, rb => f // target of operation is spilled storeAI f => r0, @a // spilled value "lives" in memory offset @r - For the use of spiled value, load value from memory into a feasible register
loadAI r0, @r => f // value lives at memory with offset @r add f, rb => ra // loaded into feasible register
- For the definition of the spilled value r (assignment of the value to virtual register r), use a feasible register f as the target register; assign fixed memory location for spilled value (spill offset @r), and store feasible register value to the address offset @r
Bottom-up Allocator
The idea:
- Focus on replacement rather than allocation
- Keep values “used soon” in registers
- Only parts of a live range may be assigned to a physical register (!= top down allocation’s “all-or-nothing” approach)
Algorithm:
- Start with an empty register set
- Load on demand
- When no register is available, free one
Replacement (heuristic):
- Spill the value whose next use is farthest in the future
- Sound familiar? Think page replacement …