packages feed

lhc-0.10: src/Grin/HPT/Lower.hs

{-# LANGUAGE OverloadedStrings #-}
module Grin.HPT.Lower
    ( lower
    ) where

import Grin.Types as Grin

import qualified Data.Map as Map
import qualified Data.Set as Set
import Control.Monad.State
import Control.Monad.Writer
import Data.List (delete)


import Grin.HPT.Environment (Lhs(..))
import Grin.HPT.Interface as Interface

type M a = State (HeapAnalysis, Int) a

lower :: HeapAnalysis -> Grin -> (Grin, HeapAnalysis)
lower hpt grin
    = case runState worker (hpt, grinUnique grin) of
        (grin, (hpt',_newUnique)) -> (grin, hpt')
    where worker = do fns <- mapM lowerFuncDef (grinFunctions grin)
                      unique <- gets snd
                      return grin{ grinFunctions = fns
                                 , grinUnique    = unique }

lowerFuncDef :: FuncDef -> M FuncDef
lowerFuncDef func
    = do body <- lowerExpression (funcDefBody func)
         return $ func{funcDefBody = body}

lowerExpression :: Expression -> M Expression
lowerExpression (a :>>= lam)
    = do a' <- lowerExpression a
         lam' <- lowerLambda lam
         return $ a' :>>= lam'
lowerExpression (a :>> b)
    = do a' <- lowerExpression a
         b' <- lowerExpression b
         return $ a' :>> b'
lowerExpression (Application (Builtin "eval") [a])
    = do f <- newVariable
         hpt <- gets fst
         case lookupHeap a hpt of
           Interface.Empty -> return $ Application (Builtin "unreachable") []
           rhs@Other{rhsTagged = nodes}
             -> do let tags = Map.toList nodes
                   addHPTInfo (VarEntry f) rhs -- (Tagged nodes)
                   alts <- mapM (mkApplyAlt []) tags
                   v <- newVariable
                   let expand ((tag,FunctionNode,0),_args) = lookupLhs (VarEntry tag) hpt
                       expand (node,args) = Other (Map.singleton node args) []
                       expanded = mconcat $ map expand tags
                   addHPTInfo (VarEntry v) expanded
                   let anyShared = heapIsShared a hpt
                   u <- mkUpdate anyShared a f v tags expanded
                   return $ Application (Builtin "fetch") [a] :>>= f :->
                            Case f alts :>>= v :->
                            u :>>
                            Unit (Variable v)
lowerExpression (Application (Builtin "apply") [a,b])
    = do hpt <- gets fst
         case lookupLhs (VarEntry a) hpt of
           Other{rhsTagged = nodes} -> do alts <- mapM (mkApplyAlt [b]) (Map.toList nodes)
                                          return $ Case a alts
           Interface.Empty -> return $ Application (Builtin "unreachable") []
lowerExpression (Application fn args)
    = return $ Application fn args
lowerExpression (Update size ptr val)
    = return $ Update size ptr val
lowerExpression (Case scrut alts)
    = do hpt <- gets fst
         let rhs = lookupLhs (VarEntry scrut) hpt
         alts' <- mapM lowerAlt (filter (`isMemberOf` rhs) alts)
         return $ Case scrut alts'
lowerExpression (Store val)
    = return $ Store val
lowerExpression (Unit val) = return $ Unit val

lowerLambda :: Lambda -> M Lambda
lowerLambda (a :-> b)
    = do b' <- lowerExpression b
         return $ a :-> b'

lowerAlt :: Alt -> M Alt
lowerAlt (a :> b)
    = do b' <- lowerExpression b
         return $ a :> b'

(Node tag nt missing args :> _) `isMemberOf` (Other{rhsTagged = nodes})
    = (tag, nt, missing) `Map.member` nodes
_ `isMemberOf` rhs = True


mkUpdate :: Bool -> Renamed -> Renamed -> Renamed ->[(Node, [Rhs])] -> Rhs -> M Expression
mkUpdate False ptr scrut val tags _ = return $ Unit Grin.Empty
mkUpdate shared ptr scrut val tags _ -- (Other{rhsTagged = expanded})
    = do hpt <- gets fst
         let doUpdate tag = case lookupLhs (VarEntry tag) hpt of
                              Other{rhsTagged = expanded} -> do alts <- mapM (uWorker val) (Map.toList expanded)
                                                                return $ Case val alts
                              _ -> return $ Unit Grin.Empty
             uWorker val ((tag, nt, missing), args)
                  = do args' <- replicateM (length args) newVariable
                       node <- newVariable
                       addHPTInfo (VarEntry node) (Other (Map.singleton (tag, nt, missing) args) [])
                       return (Node tag nt missing args' :> Update (length args'+1) ptr val)
         let worker ((tag, FunctionNode, 0), args)
                 = do args' <- replicateM (length args) newVariable
                      u <- doUpdate tag
                      return $ Node tag FunctionNode 0 args' :> u
             worker ((tag, nt, missingArgs), args)
                 = do args' <- replicateM (length args) newVariable
                      return $ Node tag nt missingArgs args' :> Unit Grin.Empty
         alts' <- mapM worker tags
         return $ Case scrut alts'
mkUpdate shared ptr scrut val tags _ = return $ Unit Grin.Empty

mkApplyAlt :: [Renamed] -> (Node, [Rhs]) -> M Alt
mkApplyAlt extraArgs ((tag, FunctionNode, n), argsRhs) | n == length extraArgs
    = do args <- replicateM (length argsRhs) newVariable
         return $ Node tag FunctionNode n args :> Application tag (args ++ extraArgs)
mkApplyAlt [extraArg] ((tag, nt, 0), argsRhs)
    = return $ Node tag nt 0 [] :> Application (Builtin "unreachable") []
mkApplyAlt extraArgs ((tag, nt, n), argsRhs)
    = do args <- replicateM (length argsRhs) newVariable
         return $ Node tag nt n args :> Unit (Node tag nt (n - length extraArgs) (args ++ extraArgs))
mkApplyAlt _ val = error $ "Grin.HPT.Lower.mkApplyAlt: unexpected tag: " ++ show val

addHPTInfo :: Lhs -> Rhs -> M ()
addHPTInfo lhs rhs
    = modify $ \(hpt, unique) -> (hptAddBinding lhs rhs hpt, unique)

newVariable :: M Renamed
newVariable = do unique <- gets snd
                 modify $ \st -> (fst st, unique + 1)
                 return $ Anonymous unique