WidgetRattus-0.1.0.1: src/AsyncRattus/Plugin/Transform.hs
{-# LANGUAGE TupleSections #-}
module AsyncRattus.Plugin.Transform (
transform
) where
import GHC.Core.Opt.Monad
import GHC.Plugins
import AsyncRattus.Plugin.PrimExpr
import AsyncRattus.Plugin.Utils
import Data.Maybe (fromJust)
import Prelude hiding ((<>))
import Data.Functor ((<&>))
import Control.Applicative ((<|>))
import Data.Tuple (swap)
data Ctx = Ctx {
fresh :: Maybe Var
}
emptyCtx :: Ctx
emptyCtx = Ctx {
fresh = Nothing
}
replaceVar :: Var -> Var -> Expr Var -> Expr Var
replaceVar match rep (Var v) = if v == match then Var rep else Var v
replaceVar match rep (App e e') = App (replaceVar match rep e) (replaceVar match rep e')
replaceVar match rep (Tick _ e) = replaceVar match rep e
replaceVar match rep (Lam v e) = Lam (if v == match then rep else v) (replaceVar match rep e)
replaceVar match rep (Let (NonRec b e') e) =
Let (NonRec newB (replaceVar match rep e')) (replaceVar match rep e)
where newB = if b == match then rep else b
replaceVar match rep (Cast e _) = replaceVar match rep e
replaceVar match rep (Case e b t alts) =
Case newExpr newB t (map (\(Alt con binds expr) -> Alt con (map (\v -> if v == match then rep else v) binds) (replaceVar match rep expr)) alts)
where newExpr = replaceVar match rep e
newB = if b == match then rep else b
replaceVar _ _ e = e
transformPrim :: Ctx -> Expr Var -> CoreM (Expr Var, PrimInfo)
transformPrim ctx expr@(App e e') = case isPrimExpr expr of
Just primInfo@(AdvApp f _) -> do
varAdv' <- adv'Var
let newE = replaceVar f varAdv' e
return (App (App newE e') (Var (fromJust $ fresh ctx)), primInfo)
Just primInfo@(SelectApp f _ _) -> do
varSelect' <- select'Var
let newE = replaceVar f varSelect' e
return (App (App newE e') (Var (fromJust $ fresh ctx)), primInfo)
Just (DelayApp _ t) -> do
bigDelayVar <- bigDelay
inputValueV <- inputValueVar
let inputValueType = mkTyConTy inputValueV
inpVar <- mkSysLocalM (fsLit "inpV") inputValueType inputValueType
let ctx' = ctx {fresh = Just inpVar}
(newExpr, maybePrimInfo) <- transform' ctx' e'
let primInfo = fromJust maybePrimInfo
let lambdaExpr = Lam inpVar newExpr
clockCode <- constructClockExtractionCode primInfo
return (App (App (App (Var bigDelayVar) (Type t)) clockCode) lambdaExpr, primInfo)
Just primInfo -> do
error $ showSDocUnsafe $ text "transformPrim: Cannot transform " <> ppr (prim primInfo)
Nothing -> error "Cannot transform non-prim applications"
transformPrim _ _ = do
error "Cannot transform anything else than prim applications"
transform :: CoreExpr -> CoreM CoreExpr
transform expr = fst <$> transform' emptyCtx expr
transform' :: Ctx -> CoreExpr -> CoreM (CoreExpr, Maybe PrimInfo)
transform' ctx expr@(App e e') = case isPrimExpr expr of
Just (BoxApp _) -> do
(newExpr, primInfo) <- transform' ctx e'
return (App e newExpr, primInfo)
(Just _) -> do
(newExpr, primInfo) <- transformPrim ctx expr
return (newExpr, Just primInfo)
Nothing -> do
(newExpr, primInfo) <- transform' ctx e
(newExpr', primInfo') <- transform' ctx e'
return (App newExpr newExpr', primInfo <|> primInfo')
transform' ctx (Lam b rhs) = do
(newExpr, primInfo) <- transform' ctx rhs
return (Lam b newExpr, primInfo)
transform' ctx (Let (NonRec b rhs) e) = do
(newRhs, primInfo) <- transform' ctx rhs
(newExpr, primInfo') <- transform' ctx e
return (Let (NonRec b newRhs) newExpr, primInfo <|> primInfo')
transform' ctx (Let (Rec binds) e) = do
transformedBinds <- mapM (\(b, bindE) -> fmap (b,) (transform' ctx bindE)) binds
(e', mPi) <- transform' ctx e
let primInfos = map (\(_, (_, p)) -> p) transformedBinds
let firstPrimInfo = foldl (<|>) mPi primInfos
newBinds <- mapM (\(b, (e, _)) -> return (b, e)) transformedBinds
return (Let (Rec newBinds) e', firstPrimInfo)
transform' ctx (Case e b t alts) = do
-- The checking pass has ensured that there are not advances on different
-- clocks. Thus we can just pick the first PrimInfo we find.
(expr, primInfo) <- transform' ctx e
-- For each alternative, transform it and save the maybePrimInfo-value
transformed <- mapM (\(Alt con binds expr) -> transform' ctx expr <&> fmap (Alt con binds) . swap) alts
-- Of all the primInfos we have, pick the first one. This is safe because
-- the checking pass has ensured that the clocks of all primitives.
let firstPrimInfo = foldl (\acc (p, _) -> acc <|> p) primInfo transformed
let alts' = map snd transformed
return (Case expr b t alts', firstPrimInfo)
transform' ctx (Cast e c) = do (e' , p) <- transform' ctx e; return (Cast e' c, p)
transform' ctx (Tick t e) = do (e' , p) <- transform' ctx e; return (Tick t e', p)
transform' _ e = return (e, Nothing)
constructClockExtractionCode :: PrimInfo -> CoreM CoreExpr
constructClockExtractionCode (AdvApp _ arg) = createClockCode arg
constructClockExtractionCode (SelectApp _ arg arg2) = clockUnion arg arg2
constructClockExtractionCode primInfo = error $ "Cannot construct clock for prim " ++ showSDocUnsafe (ppr (prim primInfo))
createClockCode :: (Var, Type) -> CoreM CoreExpr
createClockCode (argV, argT) = do
extractClock <- extractClockVar
return $ App (App (Var extractClock) (Type argT)) (Var argV)
-- Generate code for union of two clocks.
-- clockUnion (aVar, aType) (bVar, bType) returns the AST for:
-- Set.union (extractClock aVar) (extractClock bVar)
clockUnion :: (Var,Type) -> (Var, Type) -> CoreM CoreExpr
clockUnion arg arg2 = do
clock1Code <- createClockCode arg
clock2Code <- createClockCode arg2
unionVar' <- unionVar
return $
App
(
App
(
(Var unionVar')
)
clock1Code
)
clock2Code