kempe-0.2.0.2: src/Kempe/Monomorphize.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
-- | This module is kind of half-assed. I don't have any references and it
-- depends on the inliner.
module Kempe.Monomorphize ( closedModule
, MonoM
, runMonoM
, flattenModule
, tryMono
, ConsAnn (..)
-- * Benchmark
, closure
, mkModuleMap
) where
import Control.Arrow ((&&&))
import Control.Monad ((<=<))
import Control.Monad.Except (MonadError, throwError)
import Control.Monad.State.Strict (StateT, gets, runStateT)
import Data.Bifunctor (second)
import Data.Containers.ListUtils (nubOrd)
import Data.Foldable (traverse_)
import Data.Function (on)
import Data.Functor (($>))
import qualified Data.IntMap as IM
import Data.List (elemIndex, find, groupBy, partition)
import qualified Data.Map as M
import Data.Maybe (fromMaybe, mapMaybe)
import Data.Semigroup ((<>))
import qualified Data.Set as S
import qualified Data.Text as T
import Data.Tuple (swap)
import Data.Tuple.Extra (fst3, snd3, thd3)
import Kempe.AST
import Kempe.AST.Size
import Kempe.Error
import Kempe.Name
import Kempe.Unique
import Lens.Micro (Lens')
import Lens.Micro.Mtl (modifying)
import Prettyprinter (Doc, Pretty, vsep)
import Prettyprinter.Debug
-- | New function names, keyed by name + specialized type
--
-- also max state threaded through.
data RenameEnv = RenameEnv { maxState :: Int
, fnEnv :: M.Map (Unique, StackType ()) Unique
, consEnv :: M.Map (Unique, StackType ()) (Unique, ConsAnn MonoStackType)
, szEnv :: SizeEnv
}
type MonoM = StateT RenameEnv (Either (Error ()))
prettyDumpBinds :: (Pretty b, Pretty k) => M.Map k b -> Doc a
prettyDumpBinds b = vsep (prettyBind <$> M.toList b)
maxStateLens :: Lens' RenameEnv Int
maxStateLens f s = fmap (\x -> s { maxState = x }) (f (maxState s))
consEnvLens :: Lens' RenameEnv (M.Map (Unique, StackType ()) (Unique, ConsAnn MonoStackType))
consEnvLens f s = fmap (\x -> s { consEnv = x }) (f (consEnv s))
fnEnvLens :: Lens' RenameEnv (M.Map (Unique, StackType ()) Unique)
fnEnvLens f s = fmap (\x -> s { fnEnv = x }) (f (fnEnv s))
szEnvLens :: Lens' RenameEnv SizeEnv
szEnvLens f s = fmap (\x -> s { szEnv = x }) (f (szEnv s))
runMonoM :: Int -> MonoM a -> Either (Error ()) (a, (Int, SizeEnv))
runMonoM maxI = fmap (second (maxState &&& szEnv)) . flip runStateT (RenameEnv maxI mempty mempty mempty)
freshName :: T.Text -> a -> MonoM (Name a)
freshName n ty = do
pSt <- gets maxState
Name n (Unique $ pSt + 1) ty
<$ modifying maxStateLens (+1)
tryMono :: MonadError (Error ()) m => StackType () -> m MonoStackType
tryMono (StackType _ is os) | S.null (freeVars (is ++ os)) = pure (is, os)
| otherwise = throwError $ MonoFailed ()
-- | A 'ModuleMap' is a map which retrives the 'KempeDecl' associated with
-- a given 'Name'
type ModuleMap a c b = IM.IntMap (KempeDecl a c b)
mkModuleMap :: Declarations a c b -> ModuleMap a c b
mkModuleMap = IM.fromList . concatMap toInt where
toInt d@(FunDecl _ (Name _ (Unique i) _) _ _ _) = [(i, d)]
toInt d@(ExtFnDecl _ (Name _ (Unique i) _) _ _ _) = [(i, d)]
toInt d@(TyDecl _ _ _ ds) =
let us = unUnique . unique . fst <$> ds
in (, d) <$> us
toInt _ = []
squishTypeName :: BuiltinTy -> T.Text
squishTypeName TyInt = "int"
squishTypeName TyBool = "bool"
squishTypeName TyWord = "word"
squishTypeName TyInt8 = "int8"
squishType :: KempeTy a -> T.Text
squishType (TyBuiltin _ b) = squishTypeName b
squishType (TyNamed _ (Name t _ _)) = T.toLower t
squishType TyVar{} = error "not meant to be monomorphized!"
squishType (TyApp _ ty ty') = squishType ty <> squishType ty'
squishMonoStackType :: MonoStackType -> T.Text
squishMonoStackType (is, os) = foldMap squishType is <> "TT" <> foldMap squishType os
renamePattern :: Pattern (StackType ()) (StackType ()) -> MonoM (Pattern (ConsAnn MonoStackType) (StackType ()))
renamePattern (PatternInt ty i) = pure $ PatternInt ty i
renamePattern (PatternWildcard ty) = pure $ PatternWildcard ty
renamePattern (PatternBool ty b) = pure $ PatternBool ty b
renamePattern (PatternCons ty (Name t u _)) = do
cSt <- gets consEnv
let (u', ann) = M.findWithDefault (error "Internal error? unfound constructor") (u, flipStackType ty) cSt
ann' = swap <$> ann
pure $ PatternCons ann' (Name t u' ann')
renameCase :: (Pattern (StackType ()) (StackType ()), [Atom (StackType ()) (StackType ())]) -> MonoM (Pattern (ConsAnn MonoStackType) (StackType ()), [Atom (ConsAnn MonoStackType) (StackType ())])
renameCase (p, as) = (,) <$> renamePattern p <*> traverse renameAtom as
renameAtom :: Atom (StackType ()) (StackType ()) -> MonoM (Atom (ConsAnn MonoStackType) (StackType ()))
renameAtom (AtBuiltin ty b) = pure $ AtBuiltin ty b
renameAtom (If ty as as') = If ty <$> traverse renameAtom as <*> traverse renameAtom as'
renameAtom (IntLit ty i) = pure $ IntLit ty i
renameAtom (Int8Lit ty i) = pure $ Int8Lit ty i
renameAtom (WordLit ty w) = pure $ WordLit ty w
renameAtom (BoolLit ty b) = pure $ BoolLit ty b
renameAtom (Dip ty as) = Dip ty <$> traverse renameAtom as
renameAtom (AtName ty (Name t u l)) = do
mSt <- gets fnEnv
let u' = M.findWithDefault u (u, ty) mSt
pure $ AtName ty (Name t u' l)
renameAtom (Case ty ls) = Case ty <$> traverse renameCase ls
renameAtom (AtCons ty (Name t u _)) = do
cSt <- gets consEnv
let (u', ann) = M.findWithDefault (error "Internal error? unfound constructor") (u, ty) cSt
pure $ AtCons ann (Name t u' ann)
renameDecl :: KempeDecl () (StackType ()) (StackType ()) -> MonoM (KempeDecl () (ConsAnn MonoStackType) (StackType ()))
renameDecl (FunDecl l n is os as) = FunDecl l n is os <$> traverse renameAtom as
renameDecl (Export ty abi (Name t u l)) = do
mSt <- gets fnEnv
let u' = M.findWithDefault (error "Shouldn't happen; might be user error or internal error") (u, ty) mSt
pure $ Export ty abi (Name t u' l)
renameDecl (ExtFnDecl l n tys tys' b) = pure $ ExtFnDecl l n tys tys' b
renameDecl (TyDecl l n vars ls) = pure $ TyDecl l n vars ls
-- | Call 'closedModule' and perform any necessary renamings
flattenModule :: Declarations () (StackType ()) (StackType ()) -> MonoM (Declarations () (ConsAnn MonoStackType) (StackType ()))
flattenModule = renameMonoM <=< closedModule
-- | To be called after 'closedModule'
renameMonoM :: Declarations () (StackType ()) (StackType ()) -> MonoM (Declarations () (ConsAnn MonoStackType) (StackType ()))
renameMonoM = traverse renameDecl
-- | Filter so that only the 'KempeDecl's necessary for exports are there, and
-- fan out top-level functions into all necessary specializations.
--
-- This will throw an exception on ill-typed programs.
--
-- The 'Module' returned will have to be renamed.
closedModule :: Declarations () (StackType ()) (StackType ()) -> MonoM (Declarations () (StackType ()) (StackType ()))
closedModule m = addExports <$> do
{ fn' <- traverse (uncurry specializeDecl . drop1) fnDecls
; traverse_ insTyDecl $ nubOrd (snd3 <$> tyDecls)
; ty' <- specializeTyDecls tyDecls
; pure (ty' ++ fn')
}
where addExports = (++ exportsOnly m)
key = mkModuleMap m
roots = S.toList $ closure (m, key)
gatherDecl (n@(Name _ (Unique i) _), ty) = -- TODO: findWithDefault?
case IM.lookup i key of
Just decl -> (n, decl, ty)
Nothing -> error "Internal error! module map should contain all names."
rootDecl = gatherDecl <$> roots -- FIXME: two-steps away, the roots are not monomorphized! So it tries to create specialized declarations of type a b -- a b a &c.
drop1 ~(_, y, z) = (y, z)
(tyDecls, fnDecls) = partition (isTyDecl . snd3) rootDecl
isTyDecl TyDecl{} = True
isTyDecl _ = False
-- group specializations by type name?
specializeTyDecls :: [(TyName (StackType ()), KempeDecl () (StackType ()) (StackType ()), StackType ())] -> MonoM [KempeDecl () (StackType ()) (StackType ())]
specializeTyDecls ds = traverse (uncurry mkTyDecl) processed
where toMerge = groupBy ((==) `on` snd3) ds
processed = fmap process toMerge
process tyDs@((_, x, _):_) = (x, zip (fst3 <$> tyDs) (thd3 <$> tyDs))
process [] = error "Empty group!"
isTyVar :: KempeTy a -> Bool
isTyVar TyVar{} = True
isTyVar _ = False
extrNames :: KempeTy a -> Name a
extrNames (TyVar _ n) = n
extrNames _ = error "Internal error!"
sizeLeaf :: [Name a] -- ^ Type variables as declared
-> [KempeTy a]
-> MonoM Size
sizeLeaf fv tys = do
{ let (tvs, conc) = partition isTyVar tys
; pad <- sizeStack <$> gets szEnv <*> pure conc
; let tvPrecompose = fmap (forVar . extrNames) tvs
; let tvComposed = foldr compose (const pad) tvPrecompose
; pure tvComposed
}
where
findIx x = fromMaybe (error "Internal error: can't find index of type variable.") $ elemIndex x fv
forVar n =
let i = findIx n
in (!! i)
compose sz sz' = \tys' -> sz tys' + sz' tys'
insTyDecl :: KempeDecl a c b -> MonoM ()
insTyDecl (TyDecl _ (Name _ (Unique k) _) fv leaves) = do
leafSizes <- traverse (sizeLeaf fv) (fmap snd leaves)
let consSz = \tys -> 1 + maximum (($tys) <$> leafSizes) -- for the tag
modifying szEnvLens (IM.insert k consSz)
insTyDecl _ = error "Shouldn't happen."
mkTyDecl :: KempeDecl () (StackType ()) (StackType ()) -> [(TyName (StackType ()), StackType ())] -> MonoM (KempeDecl () (StackType ()) (StackType ()))
mkTyDecl (TyDecl _ tn ns preConstrs) constrs = do
env <- gets szEnv
renCons <- traverse (\(tn', ty) -> do { ty'@(is, _) <- tryMono ty ; (, is) <$> renamedCons (tn' $> ty') ty' (ConsAnn (szType env ty') (getTag tn')) }) constrs
pure $ TyDecl () tn ns renCons
where indexAt p xs = fst $ fromMaybe (error "Internal error.") $ find (\(_, x) -> p x) (zip [0..] xs)
getTag (Name _ u _) = indexAt (== u) preIxes
preIxes = fmap (unique . fst) preConstrs
szType env (_, [o]) = size' env o
szType _ _ = error "Internal error: ill-typed constructor."
mkTyDecl _ _ = error "Shouldn't happen."
specializeDecl :: KempeDecl () (StackType ()) (StackType ()) -> StackType () -> MonoM (KempeDecl () (StackType ()) (StackType ()))
specializeDecl (FunDecl _ n _ _ as) sty = do
(Name t u newStackType@(StackType _ is os)) <- renamed n =<< tryMono sty
pure $ FunDecl newStackType (Name t u newStackType) is os as
specializeDecl (ExtFnDecl l n tys tys' b) _ = pure $ ExtFnDecl l n tys tys' b
specializeDecl (Export l abi n) _ = pure $ Export l abi n
specializeDecl TyDecl{} _ = error "Shouldn't happen."
-- leave exports and foreign imports alone (have to be monomorphic)
renamedCons :: TyName a -> MonoStackType -> (MonoStackType -> ConsAnn MonoStackType) -> MonoM (TyName (StackType ()))
renamedCons (Name t i _) sty@(is, os) fAnn = do
let t' = t <> squishMonoStackType sty
(Name _ j _) <- freshName t' sty
let newStackType = StackType S.empty is os
ann = fAnn sty
modifying consEnvLens (M.insert (i, newStackType) (j, ann))
pure (Name t' j newStackType)
-- | Insert a specialized rename.
renamed :: Name a -> MonoStackType -> MonoM (Name (StackType ()))
renamed (Name t i _) sty@(is, os) = do
let t' = t <> squishMonoStackType sty
(Name _ j _) <- freshName t' sty
let newStackType = StackType S.empty is os
modifying fnEnvLens (M.insert (i, newStackType) j)
pure (Name t' j newStackType)
closure :: (Declarations a (StackType ()) (StackType ()), ModuleMap a (StackType ()) (StackType ())) -> S.Set (Name (StackType ()), StackType ())
closure (m, key) = loop roots S.empty
where roots = S.fromList (exports m)
loop ns avoid =
let res = foldMap (step . fst) (ns S.\\ avoid)
in if res == ns
then res
else ns <> loop res (ns <> avoid)
step (Name _ (Unique i) _) =
case IM.lookup i key of
Just decl -> namesInDecl decl
Nothing -> error "Internal error! module map should contain all names."
namesInDecl :: KempeDecl a (StackType ()) (StackType ()) -> S.Set (Name (StackType ()), StackType ())
namesInDecl TyDecl{} = S.empty
namesInDecl ExtFnDecl{} = S.empty
namesInDecl Export{} = S.empty
namesInDecl (FunDecl _ _ _ _ as) = foldMap namesInAtom as
namesInAtom :: Atom (StackType ()) (StackType ()) -> S.Set (Name (StackType ()), StackType ())
namesInAtom AtBuiltin{} = S.empty
namesInAtom (If _ as as') = foldMap namesInAtom as <> foldMap namesInAtom as'
namesInAtom (Dip _ as) = foldMap namesInAtom as
namesInAtom (AtName _ n@(Name _ _ l)) = S.singleton (n, l)
namesInAtom (AtCons _ tn@(Name _ _ l)) = S.singleton (tn, l)
namesInAtom IntLit{} = S.empty
namesInAtom BoolLit{} = S.empty
namesInAtom Int8Lit{} = S.empty
namesInAtom WordLit{} = S.empty
namesInAtom (Case _ as) = foldMap namesInAtom (foldMap snd as) <> foldMap (namesInPattern . fst) as
namesInPattern :: Pattern (StackType ()) (StackType ()) -> S.Set (Name (StackType ()), StackType ())
namesInPattern (PatternCons _ tn@(Name _ _ l)) = S.singleton (tn, flipStackType l) -- we have to flipStackType here because the type is the reverse of the constructor that we want to look up
namesInPattern _ = S.empty
exports :: Declarations a c b -> [(Name b, b)]
exports = mapMaybe exportsDecl
exportsOnly :: Declarations a c b -> Declarations a c b
exportsOnly = mapMaybe getExport where
getExport d@Export{} = Just d
getExport _ = Nothing
exportsDecl :: KempeDecl a c b -> Maybe (Name b, b)
exportsDecl (Export _ _ n@(Name _ _ l)) = Just (n, l)
exportsDecl _ = Nothing