packages feed

lhc-0.10: src/Grin/Optimize/Simple.hs

{-# LANGUAGE GeneralizedNewtypeDeriving, OverloadedStrings #-}
module Grin.Optimize.Simple
    ( optimize
    ) where

import Grin.Types

import Control.Monad.Reader
import qualified Data.Map as Map
import qualified Data.Set as Set
import Data.Maybe

import Traverse

type Opt a = Reader Subst a
type Subst = Map.Map Renamed Renamed



optimize :: Grin -> Grin
optimize grin
    = grin{ grinFunctions = map simpleFuncDef (grinFunctions grin)}


simpleFuncDef :: FuncDef -> FuncDef
simpleFuncDef def
    = let simplified = runReader (simpleExpression (funcDefBody def)) Map.empty
          evaled     = runReader (evalOpt simplified) Map.empty
          applied    = runReader (evalOpt evaled) Map.empty
          fetched    = runReader (fetchOpt applied) Map.empty
          pruned     = runReader (casePruneOpt fetched) Map.empty
          vectorOpt  = runReader (vectorCaseOpt pruned) Map.empty
      in def{ funcDefBody = vectorOpt }

simpleExpression :: Expression -> Opt Expression
simpleExpression (Unit (Variable v1) :>>= v2 :-> t)
    = do v1' <- doSubst v1
         subst v2 v1' (simpleExpression t)
simpleExpression (a :>>= v1 :-> Unit (Variable v2)) | v1 == v2
    = simpleExpression a
simpleExpression ((a :>>= b :-> c) :>>= d)
    = simpleExpression (a :>>= b :-> c :>>= d)
simpleExpression ((a :>>= b :-> c) :>> d)
    = simpleExpression (a :>>= b :-> c :>> d)
simpleExpression ((a :>> b) :>>= c)
    = simpleExpression (a :>> b :>>= c)
simpleExpression ((a :>> b) :>> c)
    = simpleExpression (a :>> b :>> c)
simpleExpression (Unit Empty :>> c)
    = simpleExpression c
simpleExpression (a :>> b)
    = do a' <- simpleExpression a
         b' <- simpleExpression b
         return (a' :>> b')
simpleExpression (a :>>= b :-> c)
    = do a' <- simpleExpression a
         c' <- simpleExpression c
         return (a' :>>= b :-> c')
simpleExpression (Application fn values)
    = liftM (Application fn) $ doSubsts values
simpleExpression (Store v)
    = liftM Store $ simpleValue v
simpleExpression (Update size ptr val)
    = return (Update size) `ap` doSubst ptr `ap` doSubst val
simpleExpression (Unit value)
    = liftM Unit (simpleValue value)
simpleExpression (Case var [])
    = return $ Application (Builtin "unreachable") []
simpleExpression (Case var [Variable v :> alt])
    = simpleExpression (Unit (Variable var) :>>= v :-> alt)
simpleExpression (Case var alts) | and [ case alt of Unit ret -> ret == cond; _ -> False | cond :> alt <- alts]
    = simpleExpression (Unit (Variable var))
simpleExpression (Case val alts)
    = do val' <- doSubst val
         alts' <- mapM simpleAlt alts
         return $ Case val' alts'


simpleAlt :: Alt -> Opt Alt
simpleAlt (v :> e) = do e' <- simpleExpression e
                        return (v :> e')



simpleValue :: Value -> Opt Value
simpleValue (Variable v)
    = liftM Variable $ doSubst v
simpleValue (Node name ty missing args)
    = liftM (Node name ty missing) $ doSubsts args
simpleValue (Vector vs)
    = liftM Vector $ doSubsts vs
simpleValue v@Lit{}  = return v
simpleValue v@Hole{} = return v
simpleValue v@Empty  = return v

doSubst :: Renamed -> Opt Renamed
doSubst var
    = asks $ \m -> case Map.lookup var m of
                     Nothing     -> var
                     Just newVar -> newVar

doSubsts :: [Renamed] -> Opt [Renamed]
doSubsts = mapM doSubst

subst :: Renamed -> Renamed -> Opt a -> Opt a
subst name value = local $ Map.insert name value




-- do p <- store x 
--    y <- fetch p
--    m
--  >>>
-- do y <- unit x
--    m
type FetchOpt a = Reader Heap a
type Heap = Map.Map (Either Renamed Renamed) Expression

fetchOpt :: Expression -> FetchOpt Expression
fetchOpt e@(Store val :>>= bind :-> _)
    = local (Map.insert (Left bind) (Unit val))
            (tmapM fetchOpt e)
fetchOpt e@(Application (Builtin "fetch") [val] :>>= bind :-> _)
    = local (Map.insert (Right bind) (Unit (Variable val)))
            (tmapM fetchOpt e)
fetchOpt e@(Update size ptr val :>> _)
    = local (Map.insert (Left ptr) (Unit (Variable val)))
            (tmapM fetchOpt e)
fetchOpt e@(Application (Builtin "fetch") [ptr])
    = do mbVal <- asks $ Map.lookup (Left ptr)
         case mbVal of
           Nothing -> return e
           Just e' -> return e'
fetchOpt e@(Store (Variable val))
    = do mbVal <- asks $ Map.lookup (Right val)
         case mbVal of
           Nothing -> return e
           Just e' -> return e'
fetchOpt e = tmapM fetchOpt e


type EvalOpt a = Reader (Map.Map Renamed Value) a

evalOpt :: Expression -> EvalOpt Expression
evalOpt e@(Store val :>>= bind :-> _)
    = local (Map.insert bind val)
            (tmapM evalOpt e)
evalOpt e@(Unit val :>>= bind :-> _)
    = local (Map.insert bind val)
            (tmapM evalOpt e)
evalOpt e@(Update size ptr val :>> _)
    = local (Map.insert ptr (Variable val))
            (tmapM evalOpt e)
evalOpt e@(Application (Builtin "eval") [ptr])
    = do node <- isNode (Variable ptr)
         if node
            then return (Application (Builtin "fetch") [ptr])
            else return e
    where isNode (Node _tag FunctionNode n _args) | n >= 1
              = return True
          isNode (Node _tag ConstructorNode _n _args)
              = return True
          isNode (Variable v)
              = do mbVal <- asks $ Map.lookup v
                   case mbVal of
                     Nothing  -> return False
                     Just val -> isNode val
          isNode _ = return False
evalOpt e@(Application (Builtin "apply") [fn,arg])
    = do mbNode <- getNode (Variable fn)
         case mbNode of
           Just (Node _tag FunctionNode 0 _args) -> error "Grin.Optimize.Simple.applyOpt: Invalid application."
           Just (Node tag FunctionNode 1 args)
             -> return (Application tag (args ++ [arg]))
           Just (Node tag FunctionNode n args)
             -> return (Unit (Node tag FunctionNode (n-1) (args ++ [arg])))
           _ -> return e
    where getNode node@Node{}
              = return (Just node)
          getNode (Variable v)
              = do mbVal <- asks (Map.lookup fn)
                   case mbVal of
                     Nothing  -> return Nothing
                     Just val -> getNode val
          getNode _ = return Nothing
evalOpt e = tmapM evalOpt e


type ApplyOpt a = Reader (Map.Map Renamed Value) a

applyOpt :: Expression -> ApplyOpt Expression
applyOpt e@(Unit val :>>= bind :-> _)
    = local (Map.insert bind val)
            (tmapM applyOpt e)
applyOpt e@(Application (Builtin "apply") [fn,arg])
    = do mbVal <- asks (Map.lookup fn)
         case mbVal of
           Just (Node _tag FunctionNode 0 _args) -> error "Grin.Optimize.Simple.applyOpt: Invalid application."
           Just (Node tag FunctionNode 1 args)
             -> return (Application tag (args ++ [arg]))
           Just (Node tag FunctionNode n args)
             -> return (Unit (Node tag FunctionNode (n-1) (args ++ [arg])))
           _ -> return e
applyOpt e = tmapM applyOpt e



type CasePruneOpt a = Reader Cases a
type Cases = Map.Map Renamed (Set.Set Renamed)

casePruneOpt :: Expression -> CasePruneOpt Expression
casePruneOpt e@(Unit (Node tag _ _ _) :>>= v :-> _)
    = local (Map.insertWith Set.union v (Set.singleton tag))
            (tmapM casePruneOpt e)
casePruneOpt e@(Case scrut alts)
    = do mbVals <- asks $ Map.lookup scrut
         case mbVals of
           Nothing   -> tmapM casePruneOpt e
           Just vals -> let worker (Node tag _ _ _ :> _) | tag `Set.notMember` vals = Nothing
                            worker alt = Just alt
                        in tmapM casePruneOpt (Case scrut (mapMaybe worker alts))
casePruneOpt e = tmapM casePruneOpt e


type VectorCaseOpt a = Reader (Map.Map Renamed [Renamed]) a

vectorCaseOpt :: Expression -> VectorCaseOpt Expression
vectorCaseOpt e@(Unit (Vector vs) :>>= v :-> _)
    = local (Map.insert v vs)
            (tmapM vectorCaseOpt e)
vectorCaseOpt e@(Case scrut [ Vector vs :> branch ])
    = do mbVals <- asks $ Map.lookup scrut
         case mbVals of
           Nothing   -> tmapM vectorCaseOpt e
           Just vals -> tmapM vectorCaseOpt (foldr (\(v,v') r -> Unit (Variable v) :>>= v' :-> r) branch (zip vals vs))
vectorCaseOpt e = tmapM vectorCaseOpt e