futhark-0.25.3: src/Futhark/Internalise/LiftLambdas.hs
-- | Lambda-lifting of typed, monomorphic Futhark programs without
-- modules. After this pass, the program will no longer contain any
-- 'LetFun's or 'Lambda's.
module Futhark.Internalise.LiftLambdas (transformProg) where
import Control.Monad.Reader
import Control.Monad.State
import Data.Bifunctor
import Data.Foldable
import Data.List (partition)
import Data.Map.Strict qualified as M
import Data.Maybe
import Data.Set qualified as S
import Futhark.IR.Pretty ()
import Futhark.MonadFreshNames
import Futhark.Util (nubOrd)
import Language.Futhark
import Language.Futhark.Traversals
data Env = Env
{ envReplace :: M.Map VName Exp,
envVtable :: M.Map VName StructType
}
initialEnv :: Env
initialEnv = Env mempty mempty
data LiftState = State
{ stateNameSource :: VNameSource,
stateValBinds :: [ValBind],
stateGlobal :: S.Set VName
}
initialState :: VNameSource -> LiftState
initialState src = State src mempty $ S.fromList $ M.keys intrinsics
newtype LiftM a = LiftM (ReaderT Env (State LiftState) a)
deriving (Functor, Applicative, Monad, MonadReader Env, MonadState LiftState)
instance MonadFreshNames LiftM where
putNameSource src = modify $ \s -> s {stateNameSource = src}
getNameSource = gets stateNameSource
runLiftM :: VNameSource -> LiftM () -> ([ValBind], VNameSource)
runLiftM src (LiftM m) =
let s = execState (runReaderT m initialEnv) (initialState src)
in (reverse (stateValBinds s), stateNameSource s)
addValBind :: ValBind -> LiftM ()
addValBind vb = modify $ \s ->
s
{ stateValBinds = vb : stateValBinds s,
stateGlobal = foldl' (flip S.insert) (stateGlobal s) (valBindBound vb)
}
replacing :: VName -> Exp -> LiftM a -> LiftM a
replacing v e = local $ \env ->
env {envReplace = M.insert v e $ envReplace env}
bindingParams :: [VName] -> [Pat ParamType] -> LiftM a -> LiftM a
bindingParams sizes params = local $ \env ->
env
{ envVtable =
M.fromList (map (second toStruct) (foldMap patternMap params) <> map (,i64) sizes)
<> envVtable env
}
where
i64 = Scalar $ Prim $ Signed Int64
bindingLetPat :: [VName] -> Pat StructType -> LiftM a -> LiftM a
bindingLetPat sizes pat = local $ \env ->
env
{ envVtable =
M.fromList (map (second toStruct) (patternMap pat) <> map (,i64) sizes)
<> envVtable env
}
where
i64 = Scalar $ Prim $ Signed Int64
bindingForm :: LoopFormBase Info VName -> LiftM a -> LiftM a
bindingForm (For i _) = bindingLetPat [] (Id (identName i) (identType i) mempty)
bindingForm (ForIn p _) = bindingLetPat [] p
bindingForm While {} = id
toRet :: TypeBase Size u -> TypeBase Size Uniqueness
toRet = second (const Nonunique)
liftFunction :: VName -> [TypeParam] -> [Pat ParamType] -> ResRetType -> Exp -> LiftM Exp
liftFunction fname tparams params (RetType dims ret) funbody = do
-- Find free variables
vtable <- asks envVtable
let isFree v = (v,) <$> M.lookup v vtable
withTypes = mapMaybe isFree . S.toList . fvVars
let free =
let immediate_free = withTypes $ freeInExp funbody
sizes_in_free = foldMap (freeInType . snd) immediate_free
sizes =
withTypes $
sizes_in_free
<> foldMap freeInPat params
<> freeInType ret
in nubOrd $ immediate_free <> sizes
-- Those parameters that correspond to sizes must come first.
sizes_in_types =
foldMap freeInType (toStruct ret : map snd free ++ map patternStructType params)
isSize (v, _) = v `S.member` fvVars sizes_in_types
(free_dims, free_nondims) = partition isSize free
free_ts = map (second (`setUniqueness` Nonunique)) $ free_dims ++ free_nondims
addValBind $
ValBind
{ valBindName = fname,
valBindTypeParams = tparams,
valBindParams = map mkParam free_ts ++ params,
valBindRetDecl = Nothing,
valBindRetType = Info (RetType dims ret),
valBindBody = funbody,
valBindDoc = Nothing,
valBindAttrs = mempty,
valBindLocation = mempty,
valBindEntryPoint = Nothing
}
pure
$ apply
(Var (qualName fname) (Info (augType free_ts)) mempty)
$ free_dims ++ free_nondims
where
orig_type = funType params $ RetType dims ret
mkParam (v, t) = Id v (Info (toParam Observe t)) mempty
freeVar (v, t) = Var (qualName v) (Info t) mempty
augType rem_free = funType (map mkParam rem_free) $ RetType [] $ toRet orig_type
apply :: Exp -> [(VName, StructType)] -> Exp
apply f [] = f
apply f (p : rem_ps) =
let inner_ret = AppRes (augType rem_ps) mempty
inner = mkApply f [(Observe, Nothing, freeVar p)] inner_ret
in apply inner rem_ps
transformSubExps :: ASTMapper LiftM
transformSubExps = identityMapper {mapOnExp = transformExp}
transformExp :: Exp -> LiftM Exp
transformExp (AppExp (LetFun fname (tparams, params, _, Info ret, funbody) body _) _) = do
funbody' <- bindingParams (map typeParamName tparams) params $ transformExp funbody
fname' <- newVName $ "lifted_" ++ baseString fname
lifted_call <- liftFunction fname' tparams params ret funbody'
replacing fname lifted_call $ transformExp body
transformExp (Lambda params body _ (Info ret) _) = do
body' <- bindingParams [] params $ transformExp body
fname <- newVName "lifted_lambda"
liftFunction fname [] params ret body'
transformExp (AppExp (LetPat sizes pat e body loc) appres) = do
e' <- transformExp e
body' <- bindingLetPat (map sizeName sizes) pat $ transformExp body
pure $ AppExp (LetPat sizes pat e' body' loc) appres
transformExp (AppExp (Match e cases loc) appres) = do
e' <- transformExp e
cases' <- mapM transformCase cases
pure $ AppExp (Match e' cases' loc) appres
where
transformCase (CasePat case_pat case_e case_loc) =
CasePat case_pat
<$> bindingLetPat [] case_pat (transformExp case_e)
<*> pure case_loc
transformExp (AppExp (Loop sizes pat args form body loc) appres) = do
args' <- transformExp args
form' <- astMap transformSubExps form
body' <- bindingParams sizes [pat] $ bindingForm form' $ transformExp body
pure $ AppExp (Loop sizes pat args' form' body' loc) appres
transformExp e@(Var v _ _) =
-- Note that function-typed variables can only occur in expressions,
-- not in other places where VNames/QualNames can occur.
asks (fromMaybe e . M.lookup (qualLeaf v) . envReplace)
transformExp e = astMap transformSubExps e
transformValBind :: ValBind -> LiftM ()
transformValBind vb = do
e <-
bindingParams (map typeParamName $ valBindTypeParams vb) (valBindParams vb) $
transformExp (valBindBody vb)
addValBind $ vb {valBindBody = e}
{-# NOINLINE transformProg #-}
-- | Perform the transformation.
transformProg :: (MonadFreshNames m) => [ValBind] -> m [ValBind]
transformProg vbinds =
modifyNameSource $ \namesrc ->
runLiftM namesrc $ mapM_ transformValBind vbinds