summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Marlow <marlowsd@gmail.com>2012-07-31 15:48:42 +0100
committerSimon Marlow <marlowsd@gmail.com>2012-07-31 16:59:09 +0100
commitd2361423f23c3381b5a7f57c3f9e6c2448cdac80 (patch)
treee00a583ac7a1806cf0616c241c23bfa3e9937486
parent598ee1ad1b8de089a2ed207543761d617a90db52 (diff)
downloadhaskell-d2361423f23c3381b5a7f57c3f9e6c2448cdac80.tar.gz
Improve code generated when real registers are clobbered
There was a long-standing ToDo here that I just did: if a real register is clobbered by the current instruction, then we should move it to another free register rather than spilling it to memory. This case crops up more often now that the register allocator can allocate into the fixed Rn registers.
-rw-r--r--compiler/nativeGen/RegAlloc/Linear/Main.hs91
1 files changed, 59 insertions, 32 deletions
diff --git a/compiler/nativeGen/RegAlloc/Linear/Main.hs b/compiler/nativeGen/RegAlloc/Linear/Main.hs
index 44fc8ef896..1b6810ed07 100644
--- a/compiler/nativeGen/RegAlloc/Linear/Main.hs
+++ b/compiler/nativeGen/RegAlloc/Linear/Main.hs
@@ -23,16 +23,7 @@ The algorithm is roughly:
we fill in its entry in this table with the current mapping.
For each instruction:
- (a) For each real register clobbered by this instruction:
- If a temporary resides in it,
- If the temporary is live after this instruction,
- Move the temporary to another (non-clobbered & free) reg,
- or spill it to memory. Mark the temporary as residing
- in both memory and a register if it was spilled (it might
- need to be read by this instruction).
- (ToDo: this is wrong for jump instructions?)
-
- (b) For each temporary *read* by the instruction:
+ (a) For each temporary *read* by the instruction:
If the temporary does not have a real register allocation:
- Allocate a real register from the free list. If
the list is empty:
@@ -45,6 +36,26 @@ The algorithm is roughly:
(optimisation: if we can see that a real register is going to
be used soon, then don't use it for allocation).
+ (b) For each real register clobbered by this instruction:
+ If a temporary resides in it,
+ If the temporary is live after this instruction,
+ Move the temporary to another (non-clobbered & free) reg,
+ or spill it to memory. Mark the temporary as residing
+ in both memory and a register if it was spilled (it might
+ need to be read by this instruction).
+
+ (ToDo: this is wrong for jump instructions?)
+
+ We do this after step (a), because if we start with
+ movq v1, %rsi
+ which is an instruction that clobbers %rsi, if v1 currently resides
+ in %rsi we want to get
+ movq %rsi, %freereg
+ movq %rsi, %rsi -- will disappear
+ instead of
+ movq %rsi, %freereg
+ movq %freereg, %rsi
+
(c) Update the current assignment
(d) If the instruction is a branch:
@@ -446,9 +457,6 @@ genRaInsn platform block_live new_instrs block_id instr r_dying w_dying =
-- so using nub isn't a problem).
let virt_read = nub [ vr | (RegVirtual vr) <- read ]
- -- (a) save any temporaries which will be clobbered by this instruction
- clobber_saves <- saveClobberedTemps platform real_written r_dying
-
-- debugging
{- freeregs <- getFreeRegsR
assig <- getAssigR
@@ -463,10 +471,13 @@ genRaInsn platform block_live new_instrs block_id instr r_dying w_dying =
$ do
-}
- -- (b), (c) allocate real regs for all regs read by this instruction.
+ -- (a), (b) allocate real regs for all regs read by this instruction.
(r_spills, r_allocd) <-
allocateRegsAndSpill platform True{-reading-} virt_read [] [] virt_read
+ -- (a) save any temporaries which will be clobbered by this instruction
+ clobber_saves <- saveClobberedTemps platform real_written r_dying
+
-- (d) Update block map for new destinations
-- NB. do this before removing dead regs from the assignment, because
-- these dead regs might in fact be live in the jump targets (they're
@@ -559,13 +570,9 @@ releaseRegs regs = do
-- for allocateRegs on the temps *written*,
-- - clobbered regs are not allocatable.
--
--- TODO: instead of spilling, try to copy clobbered
--- temps to another register if possible.
---
-
saveClobberedTemps
- :: (Outputable instr, Instruction instr)
+ :: (Outputable instr, Instruction instr, FR freeRegs)
=> Platform
-> [RealReg] -- real registers clobbered by this instruction
-> [Reg] -- registers which are no longer live after this insn
@@ -589,19 +596,39 @@ saveClobberedTemps platform clobbered dying
return instrs
where
- clobber assig instrs []
- = return (instrs, assig)
-
- clobber assig instrs ((temp, reg) : rest)
- = do
- (spill, slot) <- spillR platform (RegReal reg) temp
-
- -- record why this reg was spilled for profiling
- recordSpill (SpillClobber temp)
-
- let new_assign = addToUFM assig temp (InBoth reg slot)
-
- clobber new_assign (spill : instrs) rest
+ clobber assig instrs []
+ = return (instrs, assig)
+
+ clobber assig instrs ((temp, reg) : rest)
+ = do
+ freeRegs <- getFreeRegsR
+ let regclass = targetClassOfRealReg platform reg
+ freeRegs_thisClass = frGetFreeRegs regclass freeRegs
+
+ case filter (`notElem` clobbered) freeRegs_thisClass of
+
+ -- (1) we have a free reg of the right class that isn't
+ -- clobbered by this instruction; use it to save the
+ -- clobbered value.
+ (my_reg : _) -> do
+ setFreeRegsR (frAllocateReg my_reg freeRegs)
+
+ let new_assign = addToUFM assig temp (InReg my_reg)
+ let instr = mkRegRegMoveInstr platform
+ (RegReal reg) (RegReal my_reg)
+
+ clobber new_assign (instr : instrs) rest
+
+ -- (2) no free registers: spill the value
+ [] -> do
+ (spill, slot) <- spillR platform (RegReal reg) temp
+
+ -- record why this reg was spilled for profiling
+ recordSpill (SpillClobber temp)
+
+ let new_assign = addToUFM assig temp (InBoth reg slot)
+
+ clobber new_assign (spill : instrs) rest