summaryrefslogtreecommitdiff
path: root/ghc/compiler/nativeGen/AsmRegAlloc.lhs
diff options
context:
space:
mode:
Diffstat (limited to 'ghc/compiler/nativeGen/AsmRegAlloc.lhs')
-rw-r--r--ghc/compiler/nativeGen/AsmRegAlloc.lhs498
1 files changed, 498 insertions, 0 deletions
diff --git a/ghc/compiler/nativeGen/AsmRegAlloc.lhs b/ghc/compiler/nativeGen/AsmRegAlloc.lhs
new file mode 100644
index 0000000000..9d11e229b0
--- /dev/null
+++ b/ghc/compiler/nativeGen/AsmRegAlloc.lhs
@@ -0,0 +1,498 @@
+%
+% (c) The AQUA Project, Glasgow University, 1993-1995
+%
+
+\begin{code}
+#include "HsVersions.h"
+#include "../../includes/platform.h"
+#include "../../includes/GhcConstants.h"
+
+module AsmRegAlloc (
+ FutureLive(..), RegLiveness(..), RegUsage(..), Reg(..),
+ MachineRegisters(..), MachineCode(..),
+
+ mkReg, runRegAllocate,
+ extractMappedRegNos,
+
+ -- And, for self-sufficiency
+ CLabel, OrdList, PrimKind, UniqSet(..), UniqFM,
+ FiniteMap, Unique
+ ) where
+
+IMPORT_Trace
+
+import CLabelInfo ( CLabel )
+import FiniteMap
+import MachDesc
+import Maybes ( maybeToBool, Maybe(..) )
+import OrdList -- ( mkUnitList, mkSeqList, mkParList, OrdList )
+import Outputable
+import Pretty
+import PrimKind ( PrimKind(..) )
+import UniqSet
+import Unique
+import Util
+
+#if ! OMIT_NATIVE_CODEGEN
+
+#if sparc_TARGET_ARCH
+import SparcCode -- ( SparcInstr, SparcRegs ) -- for specializing
+
+{-# SPECIALIZE
+ runRegAllocate :: SparcRegs -> [Int] -> (OrdList SparcInstr) -> [SparcInstr]
+ #-}
+#endif
+#if alpha_TARGET_ARCH
+import AlphaCode -- ( AlphaInstr, AlphaRegs ) -- for specializing
+
+{-# SPECIALIZE
+ runRegAllocate :: AlphaRegs -> [Int] -> (OrdList AlphaInstr) -> [AlphaInstr]
+ #-}
+#endif
+
+#endif
+
+\end{code}
+
+%************************************************************************
+%* *
+\subsection[Reg]{Real registers}
+%* *
+%************************************************************************
+
+Static Registers correspond to actual machine registers. These should
+be avoided until the last possible moment.
+
+Dynamic registers are allocated on the fly, usually to represent a single
+value in the abstract assembly code (i.e. dynamic registers are usually
+single assignment). Ultimately, they are mapped to available machine
+registers before spitting out the code.
+
+\begin{code}
+
+data Reg = FixedReg FAST_INT -- A pre-allocated machine register
+
+ | MappedReg FAST_INT -- A dynamically allocated machine register
+
+ | MemoryReg Int PrimKind -- A machine "register" actually held in a memory
+ -- allocated table of registers which didn't fit
+ -- in real registers.
+
+ | UnmappedReg Unique PrimKind -- One of an infinite supply of registers,
+ -- always mapped to one of the earlier two
+ -- before we're done.
+ -- No thanks: deriving (Eq)
+
+mkReg :: Unique -> PrimKind -> Reg
+mkReg = UnmappedReg
+
+instance Text Reg where
+ showsPrec _ (FixedReg i) = showString "%" . shows IBOX(i)
+ showsPrec _ (MappedReg i) = showString "%" . shows IBOX(i)
+ showsPrec _ (MemoryReg i _) = showString "%M" . shows i
+ showsPrec _ (UnmappedReg i _) = showString "%U" . shows i
+
+#ifdef DEBUG
+instance Outputable Reg where
+ ppr sty r = ppStr (show r)
+#endif
+
+cmpReg (FixedReg i) (FixedReg i') = cmp_ihash i i'
+cmpReg (MappedReg i) (MappedReg i') = cmp_ihash i i'
+cmpReg (MemoryReg i _) (MemoryReg i' _) = cmp_i i i'
+cmpReg (UnmappedReg u _) (UnmappedReg u' _) = cmpUnique u u'
+cmpReg r1 r2 =
+ let tag1 = tagReg r1
+ tag2 = tagReg r2
+ in
+ if tag1 _LT_ tag2 then LT_ else GT_
+ where
+ tagReg (FixedReg _) = (ILIT(1) :: FAST_INT)
+ tagReg (MappedReg _) = ILIT(2)
+ tagReg (MemoryReg _ _) = ILIT(3)
+ tagReg (UnmappedReg _ _) = ILIT(4)
+
+cmp_i :: Int -> Int -> TAG_
+cmp_i a1 a2 = if a1 == a2 then EQ_ else if a1 < a2 then LT_ else GT_
+
+cmp_ihash :: FAST_INT -> FAST_INT -> TAG_
+cmp_ihash a1 a2 = if a1 _EQ_ a2 then EQ_ else if a1 _LT_ a2 then LT_ else GT_
+
+instance Eq Reg where
+ a == b = case cmpReg a b of { EQ_ -> True; _ -> False }
+ a /= b = case cmpReg a b of { EQ_ -> False; _ -> True }
+
+instance Ord Reg where
+ a <= b = case cmpReg a b of { LT_ -> True; EQ_ -> True; GT__ -> False }
+ a < b = case cmpReg a b of { LT_ -> True; EQ_ -> False; GT__ -> False }
+ a >= b = case cmpReg a b of { LT_ -> False; EQ_ -> True; GT__ -> True }
+ a > b = case cmpReg a b of { LT_ -> False; EQ_ -> False; GT__ -> True }
+#ifdef __GLASGOW_HASKELL__
+ _tagCmp a b = case cmpReg a b of { LT_ -> _LT; EQ_ -> _EQ; GT__ -> _GT }
+#endif
+
+instance NamedThing Reg where
+ -- the *only* method that should be defined is "getTheUnique"!
+ -- (so we can use UniqFMs/UniqSets on Regs
+ getTheUnique (UnmappedReg u _) = u
+ getTheUnique (FixedReg i) = mkPseudoUnique1 IBOX(i)
+ getTheUnique (MappedReg i) = mkPseudoUnique2 IBOX(i)
+ getTheUnique (MemoryReg i _) = mkPseudoUnique3 i
+\end{code}
+
+This is the generic register allocator.
+
+%************************************************************************
+%* *
+\subsection[RegPlace]{Map Stix registers to {\em real} registers}
+%* *
+%************************************************************************
+
+An important point: The @regUsage@ function for a particular assembly language
+must not refer to fixed registers, such as Hp, SpA, etc. The source and destination
+lists should only refer to dynamically allocated registers or static registers
+from the free list. As far as we are concerned, the fixed registers simply don't
+exist (for allocation purposes, anyway).
+
+\begin{code}
+
+class MachineRegisters a where
+ mkMRegs :: [Int] -> a
+ possibleMRegs :: PrimKind -> a -> [Int]
+ useMReg :: a -> FAST_INT -> a
+ useMRegs :: a -> [Int] -> a
+ freeMReg :: a -> FAST_INT -> a
+ freeMRegs :: a -> [Int] -> a
+
+type RegAssignment = FiniteMap Reg Reg
+type RegConflicts = FiniteMap Int (UniqSet Reg)
+
+data FutureLive
+ = FL (UniqSet Reg)
+ (FiniteMap CLabel (UniqSet Reg))
+fstFL (FL a b) = a
+
+data RegHistory a
+ = RH a
+ Int
+ RegAssignment
+
+data RegFuture
+ = RF (UniqSet Reg) -- in use
+ FutureLive -- future
+ RegConflicts
+
+data RegInfo a
+ = RI (UniqSet Reg) -- in use
+ (UniqSet Reg) -- sources
+ (UniqSet Reg) -- destinations
+ [Reg] -- last used
+ RegConflicts
+
+data RegUsage
+ = RU (UniqSet Reg)
+ (UniqSet Reg)
+
+data RegLiveness
+ = RL (UniqSet Reg)
+ FutureLive
+
+class MachineCode a where
+-- OLD:
+-- flatten :: OrdList a -> [a]
+ regUsage :: a -> RegUsage
+ regLiveness :: a -> RegLiveness -> RegLiveness
+ patchRegs :: a -> (Reg -> Reg) -> a
+ spillReg :: Reg -> Reg -> OrdList a
+ loadReg :: Reg -> Reg -> OrdList a
+
+\end{code}
+
+First we try something extremely simple.
+If that fails, we have to do things the hard way.
+
+\begin{code}
+
+runRegAllocate
+ :: (MachineRegisters a, MachineCode b)
+ => a
+ -> [Int]
+ -> (OrdList b)
+ -> [b]
+
+runRegAllocate regs reserve_regs instrs =
+ case simpleAlloc of
+ Just x -> x
+ Nothing -> hairyAlloc
+ where
+ flatInstrs = flattenOrdList instrs
+ simpleAlloc = simpleRegAlloc regs [] emptyFM flatInstrs
+ hairyAlloc = hairyRegAlloc regs reserve_regs flatInstrs
+
+\end{code}
+
+Here is the simple register allocator. Just dole out registers until
+we run out, or until one gets clobbered before its last use. Don't
+do anything fancy with branches. Just pretend that you've got a block
+of straight-line code and hope for the best. Experience indicates that
+this approach will suffice for about 96 percent of the code blocks that
+we generate.
+
+\begin{code}
+
+simpleRegAlloc
+ :: (MachineRegisters a, MachineCode b)
+ => a -- registers to select from
+ -> [Reg] -- live static registers
+ -> RegAssignment -- mapping of dynamics to statics
+ -> [b] -- code
+ -> Maybe [b]
+
+simpleRegAlloc _ _ _ [] = Just []
+simpleRegAlloc free live env (instr:instrs) =
+ if null deadSrcs && maybeToBool newAlloc && maybeToBool instrs2 then
+ Just (instr3 : instrs3)
+ else
+ Nothing
+ where
+ instr3 = patchRegs instr (lookup env2)
+
+ (srcs, dsts) = case regUsage instr of { RU s d -> (uniqSetToList s, uniqSetToList d) }
+
+ lookup env x = case lookupFM env x of {Just y -> y; Nothing -> x}
+
+ deadSrcs = [r | r@(UnmappedReg _ _) <- srcs, lookup env r `not_elem` live]
+ newDsts = [r | r@(UnmappedReg _ _) <- dsts, r `not_elem` keysFM env]
+
+ newAlloc = foldr allocateNewReg (Just (free, [])) newDsts
+ (free2, new) = case newAlloc of Just x -> x
+
+ env2 = env `addListToFM` new
+
+ live2 = map snd new ++ [x | x <- live, x `not_elem` dsts]
+
+ instrs2 = simpleRegAlloc free2 live2 env2 instrs
+ instrs3 = case instrs2 of Just x -> x
+
+ allocateNewReg
+ :: MachineRegisters a
+ => Reg
+ -> Maybe (a, [(Reg, Reg)])
+ -> Maybe (a, [(Reg, Reg)])
+
+ allocateNewReg _ Nothing = Nothing
+
+ allocateNewReg d@(UnmappedReg _ pk) (Just (free, prs)) =
+ if null choices then Nothing
+ else Just (free2, prs2)
+ where
+ choices = possibleMRegs pk free
+ reg = head choices
+ free2 = free `useMReg` (case reg of {IBOX(reg2) -> reg2} )
+ prs2 = ((d, MappedReg (case reg of {IBOX(reg2) -> reg2})) : prs)
+
+\end{code}
+
+Here is the ``clever'' bit. First go backward (i.e. left), looking for
+the last use of dynamic registers. Then go forward (i.e. right), filling
+registers with static placements.
+
+\begin{code}
+
+hairyRegAlloc
+ :: (MachineRegisters a, MachineCode b)
+ => a
+ -> [Int]
+ -> [b]
+ -> [b]
+
+hairyRegAlloc regs reserve_regs instrs =
+ case mapAccumB (doRegAlloc reserve_regs)
+ (RH regs' 1 emptyFM) noFuture instrs
+ of (RH _ loc' _, _, instrs') ->
+ if loc' == 1 then instrs' else
+ case mapAccumB do_RegAlloc_Nil
+ (RH regs'' loc' emptyFM) noFuture (flattenOrdList (patchMem instrs'))
+ of ((RH _ loc'' _),_,instrs'') ->
+ if loc'' == loc' then instrs'' else panic "runRegAllocate"
+ where
+ regs' = regs `useMRegs` reserve_regs
+ regs'' = mkMRegs reserve_regs `asTypeOf` regs
+
+do_RegAlloc_Nil = doRegAlloc [] -- out here to avoid CAF (sigh)
+do_RegAlloc_Nil
+ :: (MachineRegisters a, MachineCode b)
+ => RegHistory a
+ -> RegFuture
+ -> b
+ -> (RegHistory a, RegFuture, b)
+
+noFuture :: RegFuture
+noFuture = RF emptyUniqSet (FL emptyUniqSet emptyFM) emptyFM
+\end{code}
+
+Here we patch instructions that reference ``registers'' which are really in
+memory somewhere (the mapping is under the control of the machine-specific
+code generator). We place the appropriate load sequences before any instructions
+that use memory registers as sources, and we place the appropriate spill sequences
+after any instructions that use memory registers as destinations. The offending
+instructions are rewritten with new dynamic registers, so we have to run register
+allocation again after all of this is said and done.
+
+\begin{code}
+
+patchMem
+ :: MachineCode a
+ => [a]
+ -> OrdList a
+
+patchMem cs = foldr (mkSeqList . patchMem') mkEmptyList cs
+
+patchMem'
+ :: MachineCode a
+ => a
+ -> OrdList a
+
+patchMem' instr =
+ if null memSrcs && null memDsts then mkUnitList instr
+ else mkSeqList
+ (foldr mkParList mkEmptyList loadSrcs)
+ (mkSeqList instr'
+ (foldr mkParList mkEmptyList spillDsts))
+
+ where
+ (RU srcs dsts) = regUsage instr
+
+ memToDyn (MemoryReg i pk) = UnmappedReg (mkBuiltinUnique i) pk
+ memToDyn other = other
+
+ memSrcs = [ r | r@(MemoryReg _ _) <- uniqSetToList srcs]
+ memDsts = [ r | r@(MemoryReg _ _) <- uniqSetToList dsts]
+
+ loadSrcs = map load memSrcs
+ spillDsts = map spill memDsts
+
+ load mem = loadReg mem (memToDyn mem)
+ spill mem = spillReg (memToDyn mem) mem
+
+ instr' = mkUnitList (patchRegs instr memToDyn)
+
+\end{code}
+
+\begin{code}
+
+doRegAlloc
+ :: (MachineRegisters a, MachineCode b)
+ => [Int]
+ -> RegHistory a
+ -> RegFuture
+ -> b
+ -> (RegHistory a, RegFuture, b)
+
+doRegAlloc reserved_regs free_env in_use instr = (free_env', in_use', instr')
+ where
+ (free_env', instr') = doRegAlloc' reserved_regs free_env info instr
+ (in_use', info) = getUsage in_use instr
+
+\end{code}
+
+\begin{code}
+
+getUsage
+ :: MachineCode a
+ => RegFuture
+ -> a
+ -> (RegFuture, RegInfo a)
+
+getUsage (RF next_in_use future reg_conflicts) instr =
+ (RF in_use' future' reg_conflicts',
+ RI in_use' srcs dsts last_used reg_conflicts')
+ where (RU srcs dsts) = regUsage instr
+ (RL in_use future') = regLiveness instr (RL next_in_use future)
+ live_through = in_use `minusUniqSet` dsts
+ last_used = [ r | r <- uniqSetToList srcs,
+ not (r `elementOfUniqSet` (fstFL future) || r `elementOfUniqSet` in_use)]
+ in_use' = srcs `unionUniqSets` live_through
+ reg_conflicts' = case new_conflicts of
+ [] -> reg_conflicts
+ _ -> addListToFM reg_conflicts new_conflicts
+ new_conflicts = if isEmptyUniqSet live_dynamics then []
+ else [ (r, merge_conflicts r)
+ | r <- extractMappedRegNos (uniqSetToList dsts) ]
+ merge_conflicts reg = case lookupFM reg_conflicts reg of
+ Nothing -> live_dynamics
+ Just conflicts -> conflicts `unionUniqSets` live_dynamics
+ live_dynamics = mkUniqSet
+ [ r | r@(UnmappedReg _ _) <- uniqSetToList live_through ]
+
+doRegAlloc'
+ :: (MachineRegisters a, MachineCode b)
+ => [Int]
+ -> RegHistory a
+ -> RegInfo b
+ -> b
+ -> (RegHistory a, b)
+
+doRegAlloc' reserved (RH frs loc env) (RI in_use srcs dsts lastu conflicts) instr =
+
+ (RH frs'' loc' env'', patchRegs instr dynToStatic)
+
+ where
+
+ -- free up new registers
+ free :: [Int]
+ free = extractMappedRegNos (map dynToStatic lastu)
+
+ -- (1) free registers that are used last as source operands in this instruction
+ frs_not_in_use = frs `useMRegs` (extractMappedRegNos (uniqSetToList in_use))
+ frs' = (frs_not_in_use `freeMRegs` free) `useMRegs` reserved
+
+ -- (2) allocate new registers for the destination operands
+ -- allocate registers for new dynamics
+
+ new_dynamix = [ r | r@(UnmappedReg _ _) <- uniqSetToList dsts, r `not_elem` keysFM env ]
+
+ (frs'', loc', new) = foldr allocateNewRegs (frs', loc, []) new_dynamix
+
+ env' = addListToFM env new
+
+ env'' = delListFromFM env' lastu
+
+ dynToStatic :: Reg -> Reg
+ dynToStatic dyn@(UnmappedReg _ _) =
+ case lookupFM env' dyn of
+ Just r -> r
+ Nothing -> trace "Lost register; possibly a floating point type error in a _ccall_?" dyn
+ dynToStatic other = other
+
+ allocateNewRegs
+ :: MachineRegisters a
+ => Reg -> (a, Int, [(Reg, Reg)]) -> (a, Int, [(Reg, Reg)])
+
+ allocateNewRegs d@(UnmappedReg _ pk) (fs, mem, lst) = (fs', mem', (d, f) : lst)
+ where (fs', f, mem') = case acceptable fs of
+ [] -> (fs, MemoryReg mem pk, mem + 1)
+ (IBOX(x2):_) -> (fs `useMReg` x2, MappedReg x2, mem)
+
+ acceptable regs = filter no_conflict (possibleMRegs pk regs)
+ no_conflict reg = case lookupFM conflicts reg of
+ Nothing -> True
+ Just conflicts -> not (d `elementOfUniqSet` conflicts)
+\end{code}
+
+\begin{code}
+extractMappedRegNos :: [Reg] -> [Int]
+
+extractMappedRegNos regs
+ = foldr ex [] regs
+ where
+ ex (MappedReg i) acc = IBOX(i) : acc -- we'll take it
+ ex _ acc = acc -- leave it out
+\end{code}
+
+We keep a local copy of the Prelude function \tr{notElem},
+so that it can be specialised. (Hack me gently. [WDP 94/11])
+\begin{code}
+not_elem x [] = True
+not_elem x (y:ys) = x /= y && not_elem x ys
+\end{code}