packages feed

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

module Kempe.Asm.Arm.ControlFlow ( mkControlFlow
                                 , ControlAnn (..)
                                 ) where

import           Control.Monad.State.Strict (State, evalState, gets, modify)
import           Data.Bifunctor             (first, second)
import           Data.Functor               (($>))
import qualified Data.IntSet                as IS
import qualified Data.Map                   as M
import           Data.Semigroup             ((<>))
import           Kempe.Asm.Arm.Type
import           Kempe.Asm.Type

-- map of labels by node
type FreshM = State (Int, M.Map Label Int)

runFreshM :: FreshM a -> a
runFreshM = flip evalState (0, mempty)

mkControlFlow :: [Arm AbsReg ()] -> [Arm AbsReg ControlAnn]
mkControlFlow instrs = runFreshM (broadcasts instrs *> addControlFlow instrs)

getFresh :: FreshM Int
getFresh = gets fst <* modify (first (+1))

lookupLabel :: Label -> FreshM Int
lookupLabel l = gets (M.findWithDefault (error "Internal error in control-flow graph: node label not in map.") l . snd)

broadcast :: Int -> Label -> FreshM ()
broadcast i l = modify (second (M.insert l i))

singleton :: AbsReg -> IS.IntSet
singleton = maybe IS.empty IS.singleton . toInt

-- | Can't be called on abstract registers i.e. 'DataPointer'
-- This is kinda sus but it allows us to use an 'IntSet' for liveness analysis.
toInt :: AbsReg -> Maybe Int
toInt (AllocReg i) = Just i
toInt _            = Nothing

fromList :: [AbsReg] -> IS.IntSet
fromList = foldMap singleton

addrRegs :: Addr AbsReg -> IS.IntSet
addrRegs (Reg r)          = singleton r
addrRegs (AddRRPlus r r') = fromList [r, r']
addrRegs (AddRCPlus r _)  = singleton r

-- | Annotate instructions with a unique node name and a list of all possible
-- destinations.
addControlFlow :: [Arm AbsReg ()] -> FreshM [Arm AbsReg ControlAnn]
addControlFlow [] = pure []
addControlFlow ((Label _ l):asms) = do
    { i <- lookupLabel l
    ; (f, asms') <- next asms
    ; pure (Label (ControlAnn i (f []) IS.empty IS.empty) l : asms')
    }
addControlFlow ((BranchCond _ l c):asms) = do
    { i <- getFresh
    ; (f, asms') <- next asms
    ; l_i <- lookupLabel l
    ; pure (BranchCond (ControlAnn i (f [l_i]) IS.empty IS.empty) l c : asms')
    }
addControlFlow ((BranchZero _ r l):asms) = do
    { i <- getFresh
    ; (f, asms') <- next asms
    ; l_i <- lookupLabel l
    ; pure (BranchZero (ControlAnn i (f [l_i]) (singleton r) IS.empty) r l : asms')
    }
addControlFlow ((BranchNonzero _ r l):asms) = do
    { i <- getFresh
    ; (f, asms') <- next asms
    ; l_i <- lookupLabel l
    ; pure (BranchNonzero (ControlAnn i (f [l_i]) (singleton r) IS.empty) r l : asms')
    }
addControlFlow ((BranchLink _ l):asms) = do
    { i <- getFresh
    ; nextAsms <- addControlFlow asms
    ; l_i <- lookupLabel l
    ; pure (BranchLink (ControlAnn i [l_i] IS.empty IS.empty) l : nextAsms)
    }
addControlFlow (Ret{}:asms) = do
    { i <- getFresh
    ; nextAsms <- addControlFlow asms
    ; pure (Ret (ControlAnn i [] IS.empty IS.empty) : nextAsms)
    }
addControlFlow (asm:asms) = do
    { i <- getFresh
    ; (f, asms') <- next asms
    ; pure ((asm $> ControlAnn i (f []) (uses asm) (defs asm)) : asms')
    }

uses :: Arm AbsReg ann -> IS.IntSet
uses (MovRR _ _ r)            = singleton r
uses (AddRR _ _ r r')         = fromList [r, r']
uses (SubRR _ _ r r')         = fromList [r, r']
uses (SubRC _ _ r _)          = singleton r
uses (LShiftLRR _ _ r r')     = fromList [r, r']
uses (LShiftRRR _ _ r r')     = fromList [r, r']
uses (BranchZero _ r _)       = singleton r
uses (MovRK _ r _ _)          = singleton r -- since MovRK only affects 16 bits, it depends on the previous r to be live!
uses (BranchNonzero _ r _)    = singleton r
uses (AddRC _ _ r _)          = singleton r
uses (MulRR _ _ r r')         = fromList [r, r']
uses (AndRR _ _ r r')         = fromList [r, r']
uses (OrRR _ _ r r')          = fromList [r, r']
uses (SignedDivRR _ _ r r')   = fromList [r, r']
uses (UnsignedDivRR _ _ r r') = fromList [r, r']
uses (CmpRR _ r r')           = fromList [r, r']
uses (CmpRC _ r _)            = singleton r
uses (Load _ _ a)             = addrRegs a
uses (LoadByte _ _ a)         = addrRegs a
uses (Neg _ _ r)              = singleton r
uses (MulSubRRR _ _ r r' r'') = fromList [r, r', r'']
uses (XorRR _ _ r r')         = fromList [r, r']
uses (Store _ r a)            = singleton r <> addrRegs a
uses (StoreByte _ r a)        = singleton r <> addrRegs a
uses _                        = mempty

defs :: Arm AbsReg ann -> IS.IntSet
defs (MovRR _ r _)           = singleton r
defs (MovRC _ r _)           = singleton r
defs (MovRWord _ r _)        = singleton r
defs (MovRK _ r _ _)         = singleton r
defs (AddRR _ r _ _)         = singleton r
defs (SubRR _ r _ _)         = singleton r
defs (AddRC _ r _ _)         = singleton r
defs (SubRC _ r _ _)         = singleton r
defs (LoadByte _ r _)        = singleton r
defs (LShiftRRR _ r _ _)     = singleton r
defs (MulSubRRR _ r _ _ _)   = singleton r
defs (LShiftLRR _ r _ _)     = singleton r
defs (AndRR _ r _ _)         = singleton r
defs (OrRR _ r _ _)          = singleton r
defs (MulRR _ r _ _)         = singleton r
defs (Load _ r _)            = singleton r
defs (SignedDivRR _ r _ _)   = singleton r
defs (UnsignedDivRR _ r _ _) = singleton r
defs (LoadLabel _ r _)       = singleton r
defs (CSet _ r _)            = singleton r
defs (Neg _ r _)             = singleton r
defs (XorRR _ r _ _)         = singleton r
defs _                       = mempty

next :: [Arm AbsReg ()] -> FreshM ([Int] -> [Int], [Arm AbsReg ControlAnn])
next asms = do
    nextAsms <- addControlFlow asms
    case nextAsms of
        []      -> pure (id, [])
        (asm:_) -> pure ((node (ann asm) :), nextAsms)

-- | Construct map assigning labels to their node name.
broadcasts :: [Arm reg ()] -> FreshM [Arm reg ()]
broadcasts [] = pure []
broadcasts (asm@(Label _ l):asms) = do
    { i <- getFresh
    ; broadcast i l
    ; (asm :) <$> broadcasts asms
    }
broadcasts (asm:asms) = (asm :) <$> broadcasts asms