packages feed

kempe-0.2.0.0: src/Kempe/Asm/Arm/Linear.hs

{-# LANGUAGE FlexibleContexts  #-}
{-# LANGUAGE OverloadedStrings #-}

-- | Linear scan register allocator
module Kempe.Asm.Arm.Linear ( allocRegs
                            ) where

import           Control.Monad.State.Strict (State, evalState, gets)
import           Data.Foldable              (traverse_)
import qualified Data.IntMap                as IM
import qualified Data.IntSet                as IS
import           Data.Maybe                 (fromMaybe)
import           Data.Semigroup             ((<>))
import qualified Data.Set                   as S
import           Kempe.Asm.Arm.Type
import           Kempe.Asm.Type
import           Lens.Micro                 (Lens')
import           Lens.Micro.Mtl             (modifying, (.=))

data AllocSt = AllocSt { allocs :: IM.IntMap ArmReg -- ^ Already allocated registers
                       , free   :: S.Set ArmReg -- TODO: IntSet here?
                       }

allocsLens :: Lens' AllocSt (IM.IntMap ArmReg)
allocsLens f s = fmap (\x -> s { allocs = x }) (f (allocs s))

freeLens :: Lens' AllocSt (S.Set ArmReg)
freeLens f s = fmap (\x -> s { free = x }) (f (free s))

-- | Mark all registers as free (at the beginning).
allFree :: AllocSt
allFree = AllocSt mempty allReg

allReg :: S.Set ArmReg
allReg = S.fromList [X0 .. X29] S.\\ S.singleton X19 -- don't allocate to x19 (data pointer)

type AllocM = State AllocSt

runAllocM :: AllocM a -> a
runAllocM = flip evalState allFree

allocRegs :: [Arm AbsReg Liveness] -> [Arm ArmReg ()]
allocRegs = runAllocM . traverse allocReg

new :: Liveness -> IS.IntSet
new (Liveness i o) = o IS.\\ i

done :: Liveness -> IS.IntSet
done (Liveness i o) = i IS.\\ o

freeDone :: Liveness -> AllocM ()
freeDone l = traverse_ freeReg (IS.toList absRs)
    where absRs = done l

freeReg :: Int -> AllocM ()
freeReg i = do
    xR <- findReg i
    modifying allocsLens (IM.delete i)
    modifying freeLens (S.insert xR)

assignReg :: Int -> ArmReg -> AllocM ()
assignReg i xr =
    modifying allocsLens (IM.insert i xr)

newReg :: AllocM ArmReg
newReg = do
    rSt <- gets free
    let (res', newSt) = fromMaybe err $ S.minView rSt
    -- register is no longer free
    freeLens .= newSt
    pure res'

    where err = error "(internal error) No register available."

findReg :: Int -> AllocM ArmReg
findReg i = gets
    (IM.findWithDefault (error $ "Internal error in register allocator: unfound register" ++ show i) i . allocs)

useRegInt :: Liveness -> Int -> AllocM ArmReg
useRegInt l i =
    if i `IS.member` new l
        then do { res' <- newReg ; assignReg i res' ; pure res' }
        else findReg i

useAddr :: Liveness -> Addr AbsReg -> AllocM (Addr ArmReg)
useAddr l (Reg r)           = Reg <$> useReg l r
useAddr l (AddRCPlus r c)   = AddRCPlus <$> useReg l r <*> pure c
useAddr l (AddRRPlus r0 r1) = AddRRPlus <$> useReg l r0 <*> useReg l r1

useReg :: Liveness -> AbsReg -> AllocM ArmReg
useReg l (AllocReg i) = useRegInt l i
useReg _ DataPointer  = pure X19
useReg _ LinkReg      = pure X30
useReg _ CArg0        = pure X0
useReg _ CArg1        = pure X1 -- shouldn't clobber anything because it's just used in function wrapper to push onto the kempe stack
useReg _ CArg2        = pure X2
useReg _ CArg3        = pure X3
useReg _ CArg4        = pure X4
useReg _ CArg5        = pure X5
useReg _ CArg6        = pure X6
useReg _ CArg7        = pure X7
useReg _ StackPtr     = pure SP

allocReg :: Arm AbsReg Liveness -> AllocM (Arm ArmReg ())
allocReg Ret{}                      = pure $ Ret ()
allocReg (Branch _ l)               = pure $ Branch () l
allocReg (BranchLink _ l)           = pure $ BranchLink () l
allocReg (BranchCond _ l c)         = pure $ BranchCond () l c
allocReg (Label _ l)                = pure $ Label () l
allocReg (BSLabel _ l)              = pure $ BSLabel () l
allocReg (GnuMacro _ m)             = pure $ GnuMacro () m
allocReg (BranchZero l r lbl)       = (BranchZero () <$> useReg l r <*> pure lbl) <* freeDone l
allocReg (AddRR l r0 r1 r2)         = (AddRR () <$> useReg l r0 <*> useReg l r1 <*> useReg l r2) <* freeDone l
allocReg (SubRR l r0 r1 r2)         = (SubRR () <$> useReg l r0 <*> useReg l r1 <*> useReg l r2) <* freeDone l
allocReg (MulRR l r0 r1 r2)         = (MulRR () <$> useReg l r0 <*> useReg l r1 <*> useReg l r2) <* freeDone l
allocReg (SignedDivRR l r0 r1 r2)   = (SignedDivRR () <$> useReg l r0 <*> useReg l r1 <*> useReg l r2) <* freeDone l
allocReg (UnsignedDivRR l r0 r1 r2) = (UnsignedDivRR () <$> useReg l r0 <*> useReg l r1 <*> useReg l r2) <* freeDone l
allocReg (LShiftLRR l r0 r1 r2)     = (LShiftLRR () <$> useReg l r0 <*> useReg l r1 <*> useReg l r2) <* freeDone l
allocReg (LShiftRRR l r0 r1 r2)     = (LShiftRRR () <$> useReg l r0 <*> useReg l r1 <*> useReg l r2) <* freeDone l
allocReg (AndRR l r0 r1 r2)         = (AndRR () <$> useReg l r0 <*> useReg l r1 <*> useReg l r2) <* freeDone l
allocReg (AddRC l r0 r1 c)          = (AddRC () <$> useReg l r0 <*> useReg l r1 <*> pure c) <* freeDone l
allocReg (SubRC l r0 r1 c)          = (SubRC () <$> useReg l r0 <*> useReg l r1 <*> pure c) <* freeDone l
allocReg (MovRC l r0 c)             = (MovRC () <$> useReg l r0 <*> pure c) <* freeDone l
allocReg (MovRWord l r0 w)          = (MovRWord () <$> useReg l r0 <*> pure w) <* freeDone l
allocReg (Load l r a)               = (Load () <$> useReg l r <*> useAddr l a) <* freeDone l
allocReg (LoadLabel l r lbl)        = (LoadLabel () <$> useReg l r <*> pure lbl) <* freeDone l
allocReg (MovRR l r0 r1)            = (MovRR () <$> useReg l r0 <*> useReg l r1) <* freeDone l
allocReg (CSet l r c)               = (CSet () <$> useReg l r <*> pure c) <* freeDone l
allocReg (Store l r a)              = (Store () <$> useReg l r <*> useAddr l a) <* freeDone l
allocReg (StoreByte l r a)          = (StoreByte () <$> useReg l r <*> useAddr l a) <* freeDone l
allocReg (CmpRR l r0 r1)            = (CmpRR () <$> useReg l r0 <*> useReg l r1) <* freeDone l
allocReg (Neg l r0 r1)              = (Neg () <$> useReg l r0 <*> useReg l r1) <* freeDone l
allocReg (MulSubRRR l r0 r1 r2 r3)  = (MulSubRRR () <$> useReg l r0 <*> useReg l r1 <*> useReg l r2 <*> useReg l r3) <* freeDone l
allocReg (LoadByte l r a)           = (LoadByte () <$> useReg l r <*> useAddr l a) <* freeDone l
allocReg (XorRR l r0 r1 r2)         = (XorRR () <$> useReg l r0 <*> useReg l r1 <*> useReg l r2) <* freeDone l
allocReg (OrRR l r0 r1 r2)          = (OrRR () <$> useReg l r0 <*> useReg l r1 <*> useReg l r2) <* freeDone l
allocReg (BranchNonzero l r lbl)    = (BranchNonzero () <$> useReg l r <*> pure lbl) <* freeDone l
allocReg (CmpRC l r c)              = (CmpRC () <$> useReg l r <*> pure c) <* freeDone l
allocReg (MovRK l r0 c s)           = (MovRK () <$> useReg l r0 <*> pure c <*> pure s) <* freeDone l