module Lvm.Core.Normalize (coreNormalize) where
import Lvm.Common.Id
import Lvm.Common.IdSet
import Lvm.Core.Expr
import Lvm.Core.Utils
data Env = Env NameSupply !IdSet
uniqueId :: Env -> Id
uniqueId (Env supply _) = fst (freshId supply)
splitEnv :: Env -> (Env, Env)
splitEnv (Env s d)
= let (s0,s1) = splitNameSupply s in (Env s0 d, Env s1 d)
splitEnvs :: Env -> [Env]
splitEnvs (Env s d) = map (`Env` d) (splitNameSupplies s)
isDirect :: Env -> Id -> Bool
isDirect (Env _ d) x = elemSet x d
coreNormalize :: NameSupply -> CoreModule -> CoreModule
coreNormalize supply m
= mapExprWithSupply (normDeclExpr primitives) supply m
where
primitives = externNames m
normDeclExpr :: IdSet -> NameSupply -> Expr -> Expr
normDeclExpr directs supply = normBind (Env supply directs)
normExpr :: Env -> Expr -> Expr
normExpr env expr
= let (env1,env2) = splitEnv env
expr' = normBind env1 expr
in case expr' of
Lam _ _ -> let x = uniqueId env2
in (Let (NonRec (Bind x expr')) (Var x))
_ -> expr'
normBind :: Env -> Expr -> Expr
normBind env expr
= case expr of
Let binds e -> let (env1,env2) = splitEnv env
in Let (normBinds env1 binds) (normExpr env2 e)
Match x alts -> Match x (normAlts env alts)
Lam x e -> Lam x (normBind env e)
Ap _ _ -> normAtomExpr env expr
_ -> expr
normBinds :: Env -> Binds -> Binds
normBinds
= zipBindsWith (\env x expr -> Bind x (normBind env expr)) . splitEnvs
normAlts :: Env -> Alts -> Alts
normAlts
= zipAltsWith (\env pat expr -> Alt pat (normExpr env expr)) . splitEnvs
normAtomExpr :: Env -> Expr -> Expr
normAtomExpr env expr
= let (atom,f) = normAtom env expr
in (f atom)
normAtom :: Env -> Expr -> (Expr, Expr -> Expr)
normAtom env expr
= case expr of
Match _ _ -> freshBinding
Lam _ _ -> freshBinding
Let (Strict _) _ -> freshBinding
Let binds e -> let (env1,env2) = splitEnv env
(atom,f) = normAtom env1 e
in (atom, Let (normBinds env2 binds) . f)
Ap e1 e2 -> let (env1,env2) = splitEnv env
(atom,f) = normAtom env1 e1
(arg,g) = normArg env2 e2
in (Ap atom arg, f . g)
_ -> (expr,id)
where
freshBinding = let (env1,env2) = splitEnv env
expr' = normBind env1 expr
x = uniqueId env2
in (Var x, Let (NonRec (Bind x expr')))
normArg :: Env -> Expr -> (Expr, Expr -> Expr)
normArg env expr
= let (env1,env2) = splitEnv env
(atom,f) = normAtom env1 expr
in if isDirectAp env atom
then let x = uniqueId env2
in (Var x, f . Let (NonRec (Bind x atom)))
else (atom,f)
isDirectAp :: Env -> Expr -> Bool
isDirectAp env expr
= case expr of
Ap e1 _ -> isDirectAp env e1
Var x -> isDirect env x
_ -> False