kempe-0.2.0.0: src/Kempe/Asm/X86/ControlFlow.hs
module Kempe.Asm.X86.ControlFlow ( mkControlFlow
, ControlAnn (..)
) where
-- seems to pretty clearly be faster
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.Type
import Kempe.Asm.X86.Type
-- map of labels by node
type FreshM = State (Int, M.Map Label Int) -- TODO: map int to asm
runFreshM :: FreshM a -> a
runFreshM = flip evalState (0, mempty)
mkControlFlow :: [X86 AbsReg ()] -> [X86 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
-- | Make sure 8-bit and 64-bit registers have no overlap.
--
-- Also can't be called on abstract registers i.e. 'DataPointer' or 'CArg1'.
-- This is kinda sus but it allows us to use an 'IntSet' for liveness analysis.
toInt :: AbsReg -> Maybe Int
toInt (AllocReg64 i) = Just i
toInt (AllocReg8 i) = Just i
toInt _ = Nothing
fromList :: [AbsReg] -> IS.IntSet
fromList = foldMap singleton
addrRegs :: Addr AbsReg -> IS.IntSet
addrRegs (Reg r) = singleton r
addrRegs (AddrRRPlus r r') = fromList [r, r']
addrRegs (AddrRCPlus r _) = singleton r
addrRegs (AddrRCMinus r _) = singleton r
addrRegs (AddrRRScale r r' _) = fromList [r, r']
-- | Annotate instructions with a unique node name and a list of all possible
-- destinations.
addControlFlow :: [X86 AbsReg ()] -> FreshM [X86 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 ((Je _ l):asms) = do
{ i <- getFresh
; (f, asms') <- next asms
; l_i <- lookupLabel l -- TODO: is this what's wanted?
; pure (Je (ControlAnn i (f [l_i]) IS.empty IS.empty) l : asms')
}
addControlFlow ((Jl _ l):asms) = do
{ i <- getFresh
; (f, asms') <- next asms
; l_i <- lookupLabel l
; pure (Jl (ControlAnn i (f [l_i]) IS.empty IS.empty) l : asms')
}
addControlFlow ((Jle _ l):asms) = do
{ i <- getFresh
; (f, asms') <- next asms
; l_i <- lookupLabel l
; pure (Jle (ControlAnn i (f [l_i]) IS.empty IS.empty) l : asms')
}
addControlFlow ((Jne _ l):asms) = do
{ i <- getFresh
; (f, asms') <- next asms
; l_i <- lookupLabel l
; pure (Jne (ControlAnn i (f [l_i]) IS.empty IS.empty) l : asms')
}
addControlFlow ((Jge _ l):asms) = do
{ i <- getFresh
; (f, asms') <- next asms
; l_i <- lookupLabel l
; pure (Jge (ControlAnn i (f [l_i]) IS.empty IS.empty) l : asms')
}
addControlFlow ((Jg _ l):asms) = do
{ i <- getFresh
; (f, asms') <- next asms
; l_i <- lookupLabel l
; pure (Jg (ControlAnn i (f [l_i]) IS.empty IS.empty) l : asms')
}
addControlFlow ((Jump _ l):asms) = do
{ i <- getFresh
; nextAsms <- addControlFlow asms
; l_i <- lookupLabel l
; pure (Jump (ControlAnn i [l_i] IS.empty IS.empty) l : nextAsms)
}
addControlFlow ((Call _ l):asms) = do
{ i <- getFresh
; nextAsms <- addControlFlow asms
; l_i <- lookupLabel l
; pure (Call (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 :: X86 AbsReg ann -> IS.IntSet
uses (PushReg _ r) = singleton r
uses (PushMem _ a) = addrRegs a
uses (PopMem _ a) = addrRegs a
uses (MovRA _ _ a) = addrRegs a
uses (MovAR _ a r) = singleton r <> addrRegs a
uses (MovRR _ _ r) = singleton r
uses (MovRRLower _ _ r) = singleton r
uses (AddRR _ r r') = fromList [r, r']
uses (SubRR _ r r') = fromList [r, r']
uses (ImulRR _ r r') = fromList [r, r']
uses (AddRC _ r _) = singleton r
uses (SubRC _ r _) = singleton r
uses (AddAC _ a _) = addrRegs a
uses (MovABool _ a _) = addrRegs a
uses (MovAC _ a _) = addrRegs a
uses (MovACi8 _ a _) = addrRegs a
uses (XorRR _ r r') = fromList [r, r']
uses (CmpAddrReg _ a r) = singleton r <> addrRegs a
uses (CmpRegReg _ r r') = fromList [r, r']
uses (CmpRegBool _ r _) = singleton r
uses (CmpAddrBool _ a _) = addrRegs a
uses (LShiftLRR _ r r') = fromList [r, r']
uses (LShiftRRR _ r r') = fromList [r, r']
uses (AShiftRRR _ r r') = fromList [r, r']
uses (MovRCi8 _ r _) = singleton r
uses (MovACTag _ a _) = addrRegs a
uses (IdivR _ r) = singleton r
uses (DivR _ r) = singleton r
uses Cqo{} = IS.empty -- TODO?
uses (AndRR _ r r') = fromList [r, r']
uses (OrRR _ r r') = fromList [r, r']
uses (PopcountRR _ _ r') = singleton r'
uses (NegR _ r) = singleton r
uses _ = IS.empty
defs :: X86 AbsReg ann -> IS.IntSet
defs (MovRA _ r _) = singleton r
defs (MovRR _ r _) = singleton r
defs (MovRRLower _ r _) = singleton r
defs (MovRC _ r _) = singleton r
defs (MovRCBool _ r _) = singleton r
defs (MovRCi8 _ r _) = singleton r
defs (MovRWord _ r _) = singleton r
defs (AddRR _ r _) = singleton r
defs (SubRR _ r _) = singleton r
defs (ImulRR _ r _) = singleton r
defs (AddRC _ r _) = singleton r
defs (SubRC _ r _) = singleton r
defs (XorRR _ r _) = singleton r
defs (MovRL _ r _) = singleton r
defs (LShiftRRR _ r _) = singleton r
defs (PopReg _ r) = singleton r
defs (LShiftLRR _ r _) = singleton r
defs (AShiftRRR _ r _) = singleton r
defs (AndRR _ r _) = singleton r
defs (OrRR _ r _) = singleton r
defs (PopcountRR _ r _) = singleton r
defs (NegR _ r) = singleton r
defs (MovRCTag _ r _) = singleton r
-- defs for IdivR &c.?
defs _ = IS.empty
next :: [X86 AbsReg ()] -> FreshM ([Int] -> [Int], [X86 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 :: [X86 reg ()] -> FreshM [X86 reg ()]
broadcasts [] = pure []
broadcasts (asm@(Label _ l):asms) = do
{ i <- getFresh
; broadcast i l
; (asm :) <$> broadcasts asms
}
broadcasts (asm:asms) = (asm :) <$> broadcasts asms