lvmlib-1.0: Lvm/Core/Lift.hs
--------------------------------------------------------------------------------
-- Copyright 2001-2012, Daan Leijen, Bastiaan Heeren, Jurriaan Hage. This file
-- is distributed under the terms of the BSD3 License. For more information,
-- see the file "LICENSE.txt", which is included in the distribution.
--------------------------------------------------------------------------------
-- $Id: Lift.hs 291 2012-11-08 11:27:33Z heere112 $
----------------------------------------------------------------
-- Do "johnson" style lambda lifting
-- After this pass, each binding has either no free variables or no arguments.
-- maintains free variable information & normalised structure
----------------------------------------------------------------
module Lvm.Core.Lift (coreLift) where
import Data.List
import Data.Maybe
import Lvm.Common.Id
import Lvm.Common.IdMap
import Lvm.Common.IdSet
import Lvm.Core.Expr
import Lvm.Core.FreeVar
import Lvm.Core.Utils
----------------------------------------------------------------
-- The environment maps variables to variables that should
-- be supplied as arguments at each call site
----------------------------------------------------------------
data Env = Env IdSet (IdMap [Id]) -- primitives && the free variables to be passed as arguments
elemFree :: Env -> Id -> Bool
elemFree (Env _ env) x
= elemMap x env
lookupFree :: Env -> Id -> [Id]
lookupFree (Env _ env) x
= fromMaybe [] (lookupMap x env)
isPrimitive :: Env -> Id -> Bool
isPrimitive (Env prim _) = (`elemSet` prim)
extendFree :: Env -> Id -> [Id] -> Env
extendFree (Env prim env) x fv
= Env prim (extendMap x fv env)
----------------------------------------------------------------
-- coreLift
-- pre: [coreFreeVar] each binding is annotated with free variables
-- [coreNoShadow] there is no shadowing
----------------------------------------------------------------
coreLift :: CoreModule -> CoreModule
coreLift m
= fmap (liftExpr globals (Env primitives emptyMap)) m
where
primitives = externNames m
globals = globalNames m
liftExpr :: IdSet -> Env -> Expr -> Expr
liftExpr globals env expr
= case expr of
Let binds e
-> let (binds',env') = liftBinds globals env binds
in Let binds' (liftExpr globals env' e)
Match x alts
-> Match x (liftAlts globals env alts)
Lam x e
-> Lam x (liftExpr globals env e)
Ap expr1 expr2
-> Ap (liftExpr globals env expr1) (liftExpr globals env expr2)
Var x
-> foldl' (\e v -> Ap e (Var v)) expr (lookupFree env x)
Con (ConTag tag arity)
-> Con (ConTag (liftExpr globals env tag) arity)
_
-> expr
liftAlts :: IdSet -> Env -> Alts -> Alts
liftAlts globals env = mapAlts (\pat expr -> Alt pat (liftExpr globals env expr))
----------------------------------------------------------------
-- Lift binding groups
----------------------------------------------------------------
liftBinds :: IdSet -> Env -> Binds -> (Binds, Env)
liftBinds globals env binds
= case binds of
NonRec bind -> let ([bind'],env') = liftBindsRec globals env [bind]
in (NonRec bind',env')
Rec recs -> let (recs',env') = liftBindsRec globals env recs
in (Rec recs',env')
Strict (Bind x rhs)
-> (Strict (Bind x (liftExpr globals env rhs)),env)
freeVar2 :: IdSet -> Expr -> IdSet
freeVar2 globals = (`diffSet` globals) . freeVar
liftBindsRec :: IdSet -> Env -> [Bind] -> ([Bind],Env)
liftBindsRec globals env recs
= let (ids,exprs) = unzipBinds recs
-- calculate the mutual free variables
fvmap = fixMutual (zip ids (map (liftedFreeVar env . freeVar2 globals) exprs))
-- note these recursive equations :-)
fvs = map (removeLifted env' . listFromSet . snd) fvmap
env' = foldl insertLifted env (zip recs fvs)
-- put the computed free variables back into the bindings as lambdas
recs' = zipWith (addLambdas env) fvs (zipWith Bind ids (map (liftExpr globals env') exprs))
in (recs', env')
addLambdas :: Env -> [Id] -> Bind -> Bind
addLambdas env fv bind@(Bind x expr)
| isAtomExpr env expr = bind
| otherwise = Bind x (foldr Lam expr fv)
insertLifted :: Env -> (Bind, [Id]) -> Env
insertLifted env (Bind x expr,fv)
= if isAtomExpr env expr -- || isValueExpr expr)
then env
else extendFree env x fv
removeLifted :: Env -> [Id] -> [Id]
removeLifted env = filter (not . elemFree env)
fixMutual :: [(Id,IdSet)] -> [(Id,IdSet)]
fixMutual fvmap
= let fvmap' = map addMutual fvmap
in if size fvmap' == size fvmap
then fvmap
else fixMutual fvmap'
where
addMutual (x,fv)
= (x, foldSet addLocalFree fv fv)
addLocalFree x fv0
= case lookup x fvmap of
Just fv1 -> unionSet fv0 fv1
Nothing -> fv0
size xs
= sum (map (sizeSet . snd) xs)
liftedFreeVar :: Env -> IdSet -> IdSet
liftedFreeVar env fv
= unionSet fv (setFromList (concatMap (lookupFree env) (listFromSet fv)))
----------------------------------------------------------------
-- is an expression atomic: i.e. can we generate code inplace
----------------------------------------------------------------
isAtomExpr :: Env -> Expr -> Bool
isAtomExpr env expr
= case expr of
Ap e1 e2 -> isAtomExpr env e1 && isAtomExpr env e2
Var x -> not (isPrimitive env x)
Con _ -> True
Lit _ -> True
Let binds e -> isAtomBinds env binds && isAtomExpr env e
_ -> False
isAtomBinds :: Env -> Binds -> Bool
isAtomBinds env binds
= case binds of
Strict _ -> False
NonRec (Bind _ expr) -> isAtomExpr env expr
Rec bindings -> all (isAtomExpr env) (snd (unzipBinds bindings))