ddc-core-simpl 0.2.1.2 → 0.4.3.1
raw patch · 45 files changed
Files
- DDC/Core/Analysis/Arity.hs +141/−0
- DDC/Core/Analysis/Usage.hs +292/−0
- DDC/Core/Simplifier.hs +21/−0
- DDC/Core/Simplifier/Apply.hs +322/−0
- DDC/Core/Simplifier/Base.hs +153/−0
- DDC/Core/Simplifier/Lexer.hs +84/−0
- DDC/Core/Simplifier/Parser.hs +235/−0
- DDC/Core/Simplifier/Recipe.hs +118/−0
- DDC/Core/Simplifier/Result.hs +70/−0
- DDC/Core/Transform/ANormal.hs +0/−253
- DDC/Core/Transform/AnonymizeX.hs +126/−100
- DDC/Core/Transform/Beta.hs +157/−22
- DDC/Core/Transform/Boxing.hs +390/−0
- DDC/Core/Transform/Bubble.hs +285/−0
- DDC/Core/Transform/Elaborate.hs +110/−0
- DDC/Core/Transform/Eta.hs +306/−0
- DDC/Core/Transform/Flatten.hs +134/−0
- DDC/Core/Transform/FoldCase.hs +84/−0
- DDC/Core/Transform/Forward.hs +302/−0
- DDC/Core/Transform/Inline.hs +75/−0
- DDC/Core/Transform/Inline/Templates.hs +96/−0
- DDC/Core/Transform/Lambdas.hs +481/−0
- DDC/Core/Transform/Lambdas/Base.hs +103/−0
- DDC/Core/Transform/Lambdas/Lift.hs +184/−0
- DDC/Core/Transform/Namify.hs +285/−0
- DDC/Core/Transform/Prune.hs +213/−0
- DDC/Core/Transform/Rewrite.hs +546/−0
- DDC/Core/Transform/Rewrite/Disjoint.hs +202/−0
- DDC/Core/Transform/Rewrite/Env.hs +215/−0
- DDC/Core/Transform/Rewrite/Error.hs +81/−0
- DDC/Core/Transform/Rewrite/Match.hs +213/−0
- DDC/Core/Transform/Rewrite/Parser.hs +130/−0
- DDC/Core/Transform/Rewrite/Rule.hs +511/−0
- DDC/Core/Transform/Snip.hs +350/−0
- DDC/Core/Transform/Thread.hs +413/−0
- DDC/Core/Transform/TransformDownX.hs +139/−0
- DDC/Core/Transform/TransformModX.hs +45/−0
- DDC/Core/Transform/TransformUpX.hs +133/−0
- DDC/Core/Transform/TransformX.hs +0/−112
- DDC/Core/Transform/Unshare.hs +334/−0
- DDC/Type/Transform/Alpha.hs +55/−0
- DDC/Type/Transform/AnonymizeT.hs +16/−9
- DDC/Type/Transform/Rename.hs +0/−53
- LICENSE +1/−15
- ddc-core-simpl.cabal +65/−17
+ DDC/Core/Analysis/Arity.hs view
@@ -0,0 +1,141 @@++-- | Slurp out arities of function bindings.+-- and infer arities for primitives based on their types.+--+-- For function bindings the arity is the number of outer-most lambdas+-- in the definition. +--+-- For primitives, the arity is the number of function+-- constructors in its type. +--+module DDC.Core.Analysis.Arity+ ( -- * Arities map+ Arities+ , emptyArities+ , extendsArities+ , getArity++ -- * Arity analysis+ , aritiesOfModule+ , aritiesOfLets+ , aritiesOfPat+ , arityFromType+ , arityOfExp)+where+import DDC.Core.Module+import DDC.Core.Exp.Annot+import DDC.Data.ListUtils+import Control.Monad+import Data.Maybe+import qualified Data.Map as Map+++-- | Arities of named and anonymous bindings.+type Arities n = (Map.Map n Int, [Int])+++-- | Empty arities context.+emptyArities :: Arities n+emptyArities = (Map.empty, [])+++-- | Extend map with some binders and their arities.+extendsArities :: Ord n => Arities n -> [(Bind n, Int)] -> Arities n+extendsArities arity exts = foldl go arity exts+ where go (named, anon) (BNone _t, _) = (named,anon)+ go (named, anon) (BAnon _t, a) = (named, a:anon)+ go (named, anon) (BName n _t, a) = (Map.insert n a named, anon)+++-- | Look up a binder's arity from the arity map+-- or determine it from its type in the case of primops.+getArity :: Ord n => Arities n -> Bound n -> Maybe Int+getArity (named, anon) u+ = case u of+ -- Get arities of anonymous things from the stack.+ UIx ix + -> let Just x = index anon ix+ in Just x++ -- Lookup arities of named things from the stack.+ UName n -> Map.lookup n named++ -- Get a primitive's arity from its type.+ -- The arities of primitives always match their types, so this is ok.+ UPrim _ t -> arityFromType t+++-- Slurp ----------------------------------------------------------------------+-- | Slurp out arities of imports and top-level bindings from a module.+aritiesOfModule :: Ord n => Module a n -> Arities n+aritiesOfModule mm+ = let (lts, _) = splitXLets $ moduleBody mm++ aritiesLets + = concat $ catMaybes $ map aritiesOfLets lts++ aritiesImports + = catMaybes+ $ [ case arityFromType (typeOfImportValue isrc) of+ Just a -> Just (BName n (typeOfImportValue isrc), a)+ Nothing -> Nothing+ | (n, isrc) <- moduleImportValues mm ]++ in emptyArities+ `extendsArities` aritiesImports+ `extendsArities` aritiesLets+++-- | Get the arities of a `Lets`+aritiesOfLets :: Ord n => Lets a n -> Maybe [(Bind n, Int)]+aritiesOfLets ll+ = let get (b, x)+ = case arityOfExp x of+ Nothing -> Nothing+ Just a -> Just (b, a)+ in case ll of+ LLet b x -> sequence $ map get [(b, x)]+ LRec bxs -> sequence $ map get bxs+ _ -> Just []+++-- | Retrieve binders from case pattern, so we can extend the arity context.+-- We don't know anything about their values, so record as 0.+aritiesOfPat :: Pat n -> [(Bind n, Int)]+aritiesOfPat p+ = case p of+ PDefault -> []+ (PData _b bs) -> zip bs (repeat 0)+++-- | Get the arity of an expression. +arityOfExp :: Ord n => Exp a n -> Maybe Int+arityOfExp xx+ = case xx of+ -- Counting all binders, because they all correspond to XApps.+ XLam _ _ e -> liftM (+ 1) $ arityOfExp e+ XLAM _ _ e -> liftM (+ 1) $ arityOfExp e++ -- Determine a data constructor's arity from its type.+ XCon _ (DaConPrim _ t)+ -> arityFromType t++ -- Anything else we'll need to apply one at a time+ _ -> Just 0+++-- | Determine the arity of an expression by looking at its type.+-- Count all the function arrows, and foralls.+arityFromType :: Ord n => Type n -> Maybe Int+arityFromType tt+ | TForall _ t <- tt+ = case arityFromType t of+ Nothing -> Nothing+ Just a -> Just (1 + a)++ | isBot tt+ = Nothing++ | (args, _) <- takeTFunArgResult tt+ = Just (length args)+
+ DDC/Core/Analysis/Usage.hs view
@@ -0,0 +1,292 @@++-- | Annotate let bindings with how their bound variables are used.+module DDC.Core.Analysis.Usage+ ( -- * Usage map+ Used (..)+ , UsedMap (..)++ -- * Usage analysis+ , usageModule+ , usageX)+where+import DDC.Core.Module+import DDC.Core.Exp.Annot+import Data.List+import Data.Map (Map)+import qualified Data.Map as Map+import Data.Maybe (mapMaybe)+++-- Used -----------------------------------------------------------------------+-- | Tracks how a bound variable is used.+data Used+ -- | Bound variable is used as the function in an application.+ = UsedFunction++ -- | Bound variable is destructed by a case-expression.+ | UsedDestruct++ -- | Bound variable is used inside a @weakclo@ cast.+ | UsedInCast++ -- | Bound variable has an occurrence that is not one of the above.+ | UsedOcc++ -- | Usage is inside a Lambda abstraction (either type or value)+ | UsedInLambda Used++ -- | Usage is inside a case alternative.+ | UsedInAlt Used+ deriving (Eq, Show)+++-- UsedMap --------------------------------------------------------------------+-- | Map of bound name to how the variable is used.+data UsedMap n+ = UsedMap (Map n [Used])+ deriving Show+++-- | An empty usage map.+empty :: UsedMap n+empty = UsedMap (Map.empty)+++-- | Add a single usage to a usage map.+accUsed :: Ord n => Bound n -> Used -> UsedMap n -> UsedMap n+accUsed u used um@(UsedMap m)+ = case u of+ UName n -> UsedMap $ Map.insertWith (++) n [used] m + _ -> um++-- | Combine two usage maps.+plusUsedMap :: Ord n => UsedMap n -> UsedMap n -> UsedMap n+plusUsedMap (UsedMap map1) (UsedMap map2)+ = UsedMap $ Map.unionWith (++) map1 map2+++-- | Combine a list of usage maps into a single one.+sumUsedMap :: Ord n => [UsedMap n] -> UsedMap n+sumUsedMap [] = UsedMap Map.empty+sumUsedMap (m:ms)+ = foldl' plusUsedMap m ms+++-- | Remove some binds from a usage map+removeUsedMap :: Ord n => UsedMap n -> [Bind n] -> UsedMap n+removeUsedMap (UsedMap m) bs+ = UsedMap+ $ foldr Map.delete m+ $ mapMaybe takeNameOfBind bs+++-- Module ---------------------------------------------------------------------+-- | Annotate all binding occurrences of variables in an expression+-- with how they are used.+usageModule + :: Ord n+ => Module a n+ -> Module (UsedMap n, a) n+usageModule+ (ModuleCore+ { moduleName = name+ , moduleIsHeader = isHeader+ , moduleExportTypes = exportTypes+ , moduleExportValues = exportValues+ , moduleImportTypes = importTypes+ , moduleImportCaps = importCaps+ , moduleImportValues = importValues+ , moduleImportDataDefs = importDataDefs+ , moduleImportTypeDefs = importTypeDefs+ , moduleDataDefsLocal = dataDefsLocal+ , moduleTypeDefsLocal = typeDefsLocal+ , moduleBody = body })++ = ModuleCore+ { moduleName = name+ , moduleIsHeader = isHeader+ , moduleExportTypes = exportTypes+ , moduleExportValues = exportValues+ , moduleImportTypes = importTypes+ , moduleImportCaps = importCaps+ , moduleImportValues = importValues+ , moduleImportDataDefs = importDataDefs+ , moduleImportTypeDefs = importTypeDefs+ , moduleDataDefsLocal = dataDefsLocal+ , moduleTypeDefsLocal = typeDefsLocal+ , moduleBody = usageX body }+++-- Exp ------------------------------------------------------------------------+-- | Annotate all binding occurrences of variables in an expression+-- with how they are used.+usageX :: Ord n + => Exp a n + -> Exp (UsedMap n, a) n+usageX xx = snd $ usageX' xx+++usageX' :: Ord n + => Exp a n + -> (UsedMap n, Exp (UsedMap n, a) n)++usageX' xx+ = case xx of+ XVar a u+ | used <- accUsed u UsedOcc empty+ -> ( used+ , XVar (used, a) u)++ XCon a u+ -> ( empty+ , XCon (empty, a) u)++ -- Wrap usages from the body in UsedInLambda to singla that if we move+ -- the definition here then it might not be demanded at runtime.+ XLAM a b1 x2+ | ( used2, x2') <- usageX' x2+ , UsedMap us2 <- used2+ , used2' <- UsedMap (Map.map (map UsedInLambda) us2)+ , cleared <- removeUsedMap used2' [b1]+ -> ( cleared+ , XLAM (used2', a) b1 x2')++ -- Wrap usages from the body in UsedInLambda to signal that if we move+ -- the definition here then it might not be demanded at runtime.+ XLam a b1 x2+ | ( used2, x2') <- usageX' x2+ , UsedMap us2 <- used2+ , used2' <- UsedMap (Map.map (map UsedInLambda) us2)+ , cleared <- removeUsedMap used2' [b1]+ -> ( cleared+ , XLam (used2', a) b1 x2')++ XApp a x1 x2+ -- application of a function variable.+ | XVar a1 u <- x1+ , used1 <- accUsed u UsedFunction empty+ , (used2, x2') <- usageX' x2+ , used' <- used1 `plusUsedMap` used2+ -> ( used'+ , XApp (used', a) (XVar (used1, a1) u) x2')++ -- General application.+ | ( used1, x1') <- usageX' x1+ , ( used2, x2') <- usageX' x2+ , used' <- used1 `plusUsedMap` used2+ -> ( used'+ , XApp (used', a) x1' x2')++ XLet a lts x2+ | ( used1, lts') <- usageLets lts+ , ( used2, x2') <- usageX' x2+ , used' <- used1 `plusUsedMap` used2+ , cleared <- removeUsedMap used'+ $ uncurry (++) (bindsOfLets lts)+ -> ( cleared+ , XLet (used', a) lts' x2')++ -- Wrap usages in the Alts in UsedInAlt to signal that if we move+ -- the definition here then it might not be demanded at runtime.+ XCase a x1 alts+ | ( used1, x1') <- usageX' x1+ , ( usedA, alts') <- unzip $ map usageAlt alts+ , UsedMap usA <- sumUsedMap usedA+ , usedA' <- UsedMap (Map.map (map UsedInAlt) usA)+ , used' <- plusUsedMap used1 usedA'+ -> ( used'+ , XCase (used', a) x1' alts' )++ XCast a c x1+ | (used1, x1') <- usageX' x1+ , (used2, c') <- usageCast c+ , used' <- plusUsedMap used1 used2+ -> ( used'+ , XCast (used', a) c' x1')++ XType a t + -> ( empty+ , XType (empty, a) t)++ XWitness a w + | (used', w') <- usageWitness w+ -> ( used'+ , XWitness (used', a) w')+++-- | Annotate binding occurences of named variables with usage information.+usageLets + :: Ord n+ => Lets a n + -> (UsedMap n, Lets (UsedMap n, a) n)++usageLets lts+ = case lts of+ LLet b x+ | (used1', x') <- usageX' x+ -> (used1', LLet b x')++ LRec bxs+ | (bs, xs) <- unzip bxs+ , (useds', xs') <- unzip $ map usageX' xs+ , used' <- sumUsedMap useds'+ -> (used', LRec $ zip bs xs')++ LPrivate b mt bs + -> (empty, LPrivate b mt bs)+++-- | Annotate binding occurrences of named value variables with +-- usage information.+usageCast + :: Ord n+ => Cast a n+ -> (UsedMap n, Cast (UsedMap n, a) n)+usageCast cc+ = case cc of+ CastWeakenEffect eff + -> (empty, CastWeakenEffect eff)++ CastPurify w+ | (used, w') <- usageWitness w+ -> (used, CastPurify w')++ CastBox -> (empty, CastBox)+ CastRun -> (empty, CastRun)+++-- | Annotate binding occurrences of named level-0 variables with+-- usage information.+usageAlt + :: Ord n + => Alt a n + -> (UsedMap n, Alt (UsedMap n, a) n)++usageAlt (AAlt p x)+ = let (used, x') = usageX' x+ in (used, AAlt p x')+++-- | Annotate binding occurrences of named level-0 variables with+-- usage information.+usageWitness+ :: Ord n+ => Witness a n+ -> (UsedMap n, Witness (UsedMap n, a) n)++usageWitness ww+ = case ww of+ WVar a u+ -> (empty, WVar (empty, a) u)++ WCon a c+ -> (empty, WCon (empty, a) c)++ WApp a w1 w2+ | (used1, w1') <- usageWitness w1+ , (used2, w2') <- usageWitness w2+ , used' <- plusUsedMap used1 used2+ -> (empty, WApp (used', a) w1' w2')++ WType a t+ -> (empty, WType (empty, a) t)
+ DDC/Core/Simplifier.hs view
@@ -0,0 +1,21 @@++module DDC.Core.Simplifier+ ( -- * Simplifier Specifications+ Simplifier(..)++ -- * Transform Specifications+ , Transform(..)+ , InlinerTemplates+ , NamedRewriteRules++ -- * Transform Results+ , TransformResult(..)+ , TransformInfo(..)+ , resultDone++ -- * Application+ , applySimplifier+ , applySimplifierX)+where+import DDC.Core.Simplifier.Apply+import DDC.Core.Simplifier.Base
+ DDC/Core/Simplifier/Apply.hs view
@@ -0,0 +1,322 @@++-- | Application of simplifiers to modules and expressions.+module DDC.Core.Simplifier.Apply+ ( applySimplifier+ , applyTransform+ , applySimplifierX+ , applyTransformX)+where+import DDC.Data.Pretty+import DDC.Core.Module+import DDC.Core.Exp+import DDC.Core.Fragment+import DDC.Core.Simplifier.Base+import DDC.Core.Transform.AnonymizeX+import DDC.Core.Transform.Beta+import DDC.Core.Transform.Bubble+import DDC.Core.Transform.Elaborate+import DDC.Core.Transform.Eta as Eta+import DDC.Core.Transform.Flatten+import DDC.Core.Transform.Forward as Forward+import DDC.Core.Transform.Inline+import DDC.Core.Transform.Namify+import DDC.Core.Transform.Prune+import DDC.Core.Transform.Rewrite+import qualified DDC.Core.Transform.Lambdas as Lambdas+import qualified DDC.Core.Transform.Snip as Snip+import qualified DDC.Core.Transform.FoldCase as FoldCase++import DDC.Data.Name+import DDC.Type.Env (KindEnv, TypeEnv)+import Data.Typeable (Typeable)+import Control.Monad.State.Strict+import qualified DDC.Data.Pretty as P+import qualified DDC.Core.Env.EnvX as EnvX+import qualified Data.Set as Set+import Prelude hiding ((<$>))+++-- Modules --------------------------------------------------------------------+-- | Apply a simplifier to a module.+--+-- The state monad can be used by `Namifier` functions to generate fresh names.+--+applySimplifier + :: (Show a, Pretty a, Ord n, Show n, Pretty n, CompoundName n)+ => Profile n -- ^ Profile of language we're working in+ -> KindEnv n -- ^ Kind environment+ -> TypeEnv n -- ^ Type environment+ -> Simplifier s a n -- ^ Simplifier to apply+ -> Module a n -- ^ Module to simplify+ -> State s (TransformResult (Module a n))++applySimplifier !profile !kenv !tenv !spec !mm+ = let down = applySimplifier profile kenv tenv+ in case spec of+ Seq t1 t2+ -> do tm <- down t1 mm+ tm' <- down t2 (result tm)++ let info =+ case (resultInfo tm, resultInfo tm') of+ (TransformInfo i1, TransformInfo i2) -> SeqInfo i1 i2+ + let again = resultAgain tm || resultAgain tm'+ let progress = resultProgress tm || resultProgress tm'++ return TransformResult+ { result = result tm'+ , resultAgain = again+ , resultProgress = progress+ , resultInfo = TransformInfo info }++ Fix i s+ -> do tm <- applyFixpoint profile kenv tenv i s mm+ let info =+ case resultInfo tm of+ TransformInfo info1 -> FixInfo i info1+ + return TransformResult+ { result = result tm+ , resultAgain = resultAgain tm+ , resultProgress = resultProgress tm+ , resultInfo = TransformInfo info }++ Trans t1+ -> applyTransform profile kenv tenv t1 mm+++-- | Apply a transform until it stops progressing, or a maximum number of times+applyFixpoint+ :: (Show a, Pretty a, Ord n, Show n, Pretty n, CompoundName n)+ => Profile n -- ^ Profile of language we're working in+ -> KindEnv n -- ^ Kind environment+ -> TypeEnv n -- ^ Type environment+ -> Int -- ^ Maximum number of times to apply+ -> Simplifier s a n -- ^ Transform to apply.+ -> Module a n -- ^ Module to simplify.+ -> State s (TransformResult (Module a n))++applyFixpoint !profile !kenv !tenv !i' !spec !mm'+ = go i' mm' False+ where+ simp = applySimplifier profile kenv tenv spec++ go 0 mm progress + = do tm <- simp mm+ return tm { resultProgress = progress }++ go i mm progress + = do tm <- simp mm+ case resultAgain tm of+ False + -> return tm { resultProgress = progress }++ True + -> do tm' <- go (i-1) (result tm) True++ let info + = case (resultInfo tm, resultInfo tm') of+ (TransformInfo i1, TransformInfo i2)+ -> SeqInfo i1 i2++ return TransformResult+ { result = result tm'+ , resultAgain = resultProgress tm'+ , resultProgress = resultProgress tm'+ , resultInfo = TransformInfo info }++-- | Apply a transform to a module.+applyTransform+ :: (Show a, Pretty a, Ord n, Show n, Pretty n, CompoundName n)+ => Profile n -- ^ Profile of language we're working in+ -> KindEnv n -- ^ Kind environment+ -> TypeEnv n -- ^ Type environment+ -> Transform s a n -- ^ Transform to apply.+ -> Module a n -- ^ Module to simplify.+ -> State s (TransformResult (Module a n))++applyTransform !profile !_kenv !_tenv !spec !mm+ = let res x = return $ resultDone (show $ ppr spec) x+ in case spec of+ Id -> res mm+ Anonymize -> res $ anonymizeX mm+ Beta config -> return $ betaReduce profile config mm+ Bubble -> res $ bubbleModule mm+ Elaborate -> res $ elaborateModule mm+ Eta config -> return $ Eta.etaModule profile config mm+ Flatten -> res $ flatten mm++ Forward + -> let config = Forward.Config (const FloatAllow) False+ in return $ forwardModule profile config mm++ FoldCase config -> res $ FoldCase.foldCase config mm+ Inline getDef -> res $ inline getDef Set.empty mm++ Lambdas+ -> res+ $ Lambdas.evalState "l"+ $ Lambdas.lambdasModule profile mm++ Namify namK namT -> namifyUnique namK namT mm >>= res+ Prune -> res $ pruneModule profile mm+ Rewrite rules -> res $ rewriteModule rules mm+ Snip config -> res $ Snip.snip config mm+++-- Expressions ----------------------------------------------------------------+-- | Apply a simplifier to an expression.+--+-- The state monad can be used by `Namifier` functions to generate fresh names.+--+applySimplifierX + :: (Show a, Pretty a, Show n, Ord n, Pretty n, CompoundName n)+ => Profile n -- ^ Profile of language we're working in+ -> KindEnv n -- ^ Kind environment+ -> TypeEnv n -- ^ Type environment+ -> Simplifier s a n -- ^ Simplifier to apply+ -> Exp a n -- ^ Expression to simplify+ -> State s (TransformResult (Exp a n))++applySimplifierX !profile !kenv !tenv !spec !xx+ = let down = applySimplifierX profile kenv tenv+ in case spec of+ Seq t1 t2+ -> do tx <- down t1 xx+ tx' <- down t2 (result tx)++ let info =+ case (resultInfo tx, resultInfo tx') of+ (TransformInfo i1, TransformInfo i2) -> SeqInfo i1 i2+ + let again = resultAgain tx || resultAgain tx'+ let progress = resultProgress tx || resultProgress tx'++ return TransformResult+ { result = result tx'+ , resultAgain = again+ , resultProgress = progress+ , resultInfo = TransformInfo info }++ Fix i s+ -> do tx <- applyFixpointX profile kenv tenv i s xx+ let info =+ case resultInfo tx of+ TransformInfo info1 -> FixInfo i info1+ + return TransformResult+ { result = result tx+ , resultAgain = resultAgain tx+ , resultProgress = resultProgress tx+ , resultInfo = TransformInfo info }+ + Trans t1+ -> applyTransformX profile kenv tenv t1 xx+++-- | Apply a simplifier until it stops progressing, or a maximum number of times+applyFixpointX+ :: (Show a, Pretty a, Show n, Ord n, Pretty n, CompoundName n)+ => Profile n -- ^ Profile of language we're working in+ -> KindEnv n -- ^ Kind environment+ -> TypeEnv n -- ^ Type environment+ -> Int -- ^ Maximum number of times to apply+ -> Simplifier s a n -- ^ Simplifier to apply.+ -> Exp a n -- ^ Exp to simplify.+ -> State s (TransformResult (Exp a n))++applyFixpointX !profile !kenv !tenv !i' !s !xx'+ = go i' xx' False+ where+ simp = applySimplifierX profile kenv tenv s++ go 0 xx progress + = do tx <- simp xx+ return tx { resultProgress = progress }++ go i xx progress + = do tx <- simp xx+ case resultAgain tx of+ False + -> return tx { resultProgress = progress }++ True + -> do tx' <- go (i-1) (result tx) True++ let info + = case (resultInfo tx, resultInfo tx') of+ (TransformInfo i1, TransformInfo i2) + -> SeqInfo i1 i2++ return TransformResult+ { result = result tx'+ , resultAgain = resultProgress tx'+ , resultProgress = resultProgress tx'+ , resultInfo = TransformInfo info }+++-- | Result of applying two simplifiers in sequence.+data SeqInfo+ = forall i1 i2+ . (Typeable i1, Typeable i2, Pretty i1, Pretty i2)+ => SeqInfo i1 i2+ deriving Typeable+++instance Pretty SeqInfo where+ ppr (SeqInfo i1 i2) = ppr i1 P.<> text ";" <$> ppr i2+++-- | Result of applying a simplifier until we reach a fixpoint.+data FixInfo+ = forall i1+ . (Typeable i1, Pretty i1)+ => FixInfo Int i1+ deriving Typeable+++instance Pretty FixInfo where+ ppr (FixInfo num i1) + = text "fix" <+> int num P.<> text ":"+ <$> indent 4 (ppr i1)+++-- | Apply a single transform to an expression.+applyTransformX + :: (Show a, Show n, Ord n, Pretty n)+ => Profile n -- ^ Profile of language we're working in+ -> KindEnv n -- ^ Kind environment+ -> TypeEnv n -- ^ Type environment+ -> Transform s a n -- ^ Transform to apply.+ -> Exp a n -- ^ Exp to transform.+ -> State s (TransformResult (Exp a n))++applyTransformX !profile !kenv !tenv !spec !xx+ = let res x = return $ resultDone (show $ ppr spec) x+ env = EnvX.fromPrimEnvs + (profilePrimKinds profile)+ (profilePrimTypes profile)+ (profilePrimDataDefs profile)+ in case spec of+ Id -> res xx+ Anonymize -> res $ anonymizeX xx+ Beta config -> return $ betaReduce profile config xx+ Bubble -> res $ bubbleX kenv tenv xx+ Elaborate{} -> res $ elaborateX xx+ Eta config -> return $ Eta.etaX profile config env xx+ Flatten -> res $ flatten xx++ Forward + -> let config = Forward.Config (const FloatAllow) False+ in return $ forwardX profile config xx++ Inline getDef -> res $ inline getDef Set.empty xx+ FoldCase config -> res $ FoldCase.foldCase config xx+ Lambdas -> res $ xx+ Namify namK namT -> namifyUnique namK namT xx >>= res+ Prune -> return $ pruneX profile env xx+ Rewrite rules -> return $ rewriteX rules xx+ Snip config -> res $ Snip.snip config xx++
+ DDC/Core/Simplifier/Base.hs view
@@ -0,0 +1,153 @@++module DDC.Core.Simplifier.Base+ ( -- * Simplifier Specifications+ Simplifier(..)++ -- * Transform Specifications+ , Transform(..)+ , InlinerTemplates+ , NamedRewriteRules++ -- * Transform Results+ , TransformResult(..)+ , TransformInfo(..)+ , NoInformation+ , resultDone)+where+import DDC.Core.Simplifier.Result+import DDC.Core.Transform.Rewrite.Rule+import DDC.Core.Transform.Namify+import DDC.Core.Exp+import DDC.Type.Env+import DDC.Data.Pretty+import qualified DDC.Core.Transform.Snip as Snip+import qualified DDC.Core.Transform.Eta as Eta+import qualified DDC.Core.Transform.Beta as Beta+import qualified DDC.Core.Transform.FoldCase as FoldCase+++-- Simplifier -----------------------------------------------------------------+-- | Specification of how to simplify a core program.+data Simplifier s a n+ -- | Apply a single transform.+ = Trans (Transform s a n)++ -- | Apply two simplifiers in sequence.+ | Seq (Simplifier s a n) (Simplifier s a n)++ -- | Keep applying a transform until it reports that further+ -- applications won't be helpful, bailing out after a maximum number+ -- of applications.+ | Fix Int (Simplifier s a n)+++instance Monoid (Simplifier s a n) where+ mempty = Trans Id+ mappend = Seq+++instance Pretty (Simplifier s a n) where+ ppr ss+ = case ss of+ Seq s1 s2+ -> ppr s1 <+> text ";" <+> ppr s2++ Fix i s+ -> text "fix" <+> int i <+> ppr s++ Trans t1+ -> ppr t1+++-- Transform ------------------------------------------------------------------+-- | Individual transforms to apply during simplification.+data Transform s a n+ -- | The Identity transform returns the original program unharmed.+ = Id++ -- | Rewrite named binders to anonymous deBruijn binders.+ | Anonymize++ -- | Perform beta reduction when the argument is not a redex.+ | Beta Beta.Config++ -- | Float casts outwards.+ | Bubble++ -- | Elaborate possible Const and Distinct witnesses that aren't+ -- otherwise in the program.+ | Elaborate++ -- | Perform eta expansion and reduction.+ | Eta Eta.Config++ -- | Flatten nested let and case expressions.+ | Flatten++ -- | Float single-use bindings forward into their use sites.+ | Forward++ -- | Fold case expressions.+ | FoldCase FoldCase.Config++ -- | Inline definitions into their use sites.+ | Inline+ { -- | Get the unfolding for a named variable.+ transInlineDef :: InlinerTemplates a n }++ -- | Perform lambda lifting.+ | Lambdas++ -- | Rewrite anonymous binders to fresh named binders.+ | Namify+ { -- | Create a namifier to make fresh type (level-1) + -- names that don't conflict with any already in this+ -- environment.+ transMkNamifierT :: Env n -> Namifier s n++ -- | Create a namifier to make fresh value or witness (level-0) + -- names that don't conflict with any already in this+ -- environment.+ , transMkNamifierX :: Env n -> Namifier s n }++ -- | Remove unused, pure let bindings.+ | Prune++ -- | Apply general rule-based rewrites.+ | Rewrite+ { -- | List of rewrite rules along with their names.+ transRules :: NamedRewriteRules a n }++ -- | Introduce let-bindings for nested applications.+ | Snip Snip.Config+++-- | Function to get the inliner template (unfolding) for the given name.+type InlinerTemplates a n + = (n -> Maybe (Exp a n))++-- | Rewrite rules along with their names.+type NamedRewriteRules a n+ = [(String, RewriteRule a n)]+++instance Pretty (Transform s a n) where+ ppr ss+ = case ss of+ Id -> text "Id"+ Anonymize -> text "Anonymize"+ Beta{} -> text "Beta"+ Bubble -> text "Bubble"+ Elaborate -> text "Elaborate"+ Eta{} -> text "Eta"+ Flatten -> text "Flatten"+ Forward -> text "Forward"+ FoldCase{} -> text "FoldCase"+ Inline{} -> text "Inline"+ Lambdas{} -> text "Lambdas"+ Namify{} -> text "Namify"+ Prune -> text "Prune"+ Rewrite{} -> text "Rewrite"+ Snip{} -> text "Snip"+ +
+ DDC/Core/Simplifier/Lexer.hs view
@@ -0,0 +1,84 @@++module DDC.Core.Simplifier.Lexer+ ( Tok(..)+ , lexSimplifier)+where+import DDC.Data.SourcePos+import Data.Char+++lexSimplifier + :: (String -> Maybe n) -- ^ Function to read a name.+ -> String -- ^ String to parse.+ -> [Located (Tok n)]++lexSimplifier readName str+ = map (\t -> Located (SourcePos "<simplifier spec>" 0 0) t) + $ lexer readName str+++-- Lexer ----------------------------------------------------------------------+-- | Lex a transform specification.+lexer :: (String -> Maybe n) -- ^ Function to read a name.+ -> String -- ^ String to parse.+ -> [Tok n]++lexer readName ss+ = let down = lexer readName+ in case ss of+ [] -> []++ (';' : cs) -> KSemiColon : down cs+ (',' : cs) -> KComma : down cs+ ('-' : cs) -> KMinus : down cs+ ('+' : cs) -> KPlus : down cs+ ('{' : cs) -> KBraceBra : down cs+ ('}' : cs) -> KBraceKet : down cs+ ('[' : cs) -> KSquareBra : down cs+ (']' : cs) -> KSquareKet : down cs++ 'f' : 'i' : 'x' : s : cs + | isSpace s -> KFix : down cs++ (c : cs)+ | isSpace c+ -> down cs++ (c : cs)+ | isUpper c+ , (body, rest) <- span isAlpha cs+ -> KCon (c : body) : down rest++ (c : cs)+ | isLower c+ , (body, rest) <- span isAlpha cs+ , Just n <- readName (c : body)+ -> KVar n : down rest++ (c : cs)+ | isDigit c+ , (digits, rest) <- span isDigit cs+ -> KInt (read (c:digits)) : down rest++ _ -> [KJunk ss]+++-- | Tokens for transform specification.+data Tok n+ = KEnd+ | KJunk String+ | KCon String+ | KVar n+ | KInt Int++ | KFix++ | KSemiColon+ | KComma+ | KMinus+ | KPlus+ | KBraceBra+ | KBraceKet+ | KSquareBra+ | KSquareKet+ deriving (Eq, Show)
+ DDC/Core/Simplifier/Parser.hs view
@@ -0,0 +1,235 @@++module DDC.Core.Simplifier.Parser+ ( SimplifierDetails (..)+ , parseSimplifier)+where+import DDC.Core.Transform.Namify+import DDC.Core.Transform.Inline+import DDC.Core.Simplifier.Base+import DDC.Core.Module+import DDC.Type.Env+import DDC.Core.Simplifier.Lexer+import DDC.Data.SourcePos+import DDC.Control.Parser (pTok)+import Data.Set (Set)+import qualified DDC.Core.Transform.Snip as Snip+import qualified DDC.Core.Transform.Beta as Beta+import qualified DDC.Core.Transform.Eta as Eta+import qualified DDC.Core.Transform.FoldCase as FoldCase+import qualified DDC.Control.Parser as P+import qualified Data.Map as Map+import qualified Data.Set as Set+++---------------------------------------------------------------------------------------------------+-- | Auxilliary information that may be used by a simplifier.+data SimplifierDetails s a n+ = SimplifierDetails+ { -- | Create a namifier to make fresh type (level-1) + -- names that don't conflict with any already in this environment.+ simplifierMkNamifierT :: Env n -> Namifier s n++ -- | Create a namifier to make fresh value or witness (level-0) + -- names that don't conflict with any already in this environment.+ , simplifierMkNamifierX :: Env n -> Namifier s n++ -- | Rewrite rules along with their names.+ , simplifierRules :: NamedRewriteRules a n++ -- | Modules available for inlining.+ , simplifierTemplates :: [Module a n] }+++---------------------------------------------------------------------------------------------------+-- | A parser of simplifier specifications.+type Parser n a+ = P.Parser (Tok n) a++-- | Parse a simplifier from a string.+parseSimplifier+ :: (Ord n, Show n)+ => (String -> Maybe n) -- Function to read a name.+ -> SimplifierDetails s a n+ -> String+ -> Either P.ParseError (Simplifier s a n)++parseSimplifier readName details str+ = let kend = Located (SourcePos "<simplifier spec>" 0 0) KEnd+ toks = lexSimplifier readName str ++ [kend]+ in P.runTokenParser show "<simplifier spec>" + (pSimplifier details)+ toks+++-- | Parse a simplifier.+pSimplifier + :: (Ord n, Show n)+ => SimplifierDetails s a n+ -> Parser n (Simplifier s a n)++pSimplifier details+ = do simpl <- pSimplifierSeq details+ pTok KEnd+ return simpl+++-- | Parse a simplifier sequence.+pSimplifierSeq + :: (Ord n, Show n)+ => SimplifierDetails s a n+ -> Parser n (Simplifier s a n)++pSimplifierSeq details+ = P.choice+ [ do -- Single Transform or Sequence.+ simpl0 <- pSimplifier0 details++ P.choice+ [ do pTok KSemiColon+ simpl1 <- pSimplifierSeq details+ return $ Seq simpl0 simpl1++ , do return simpl0 ]+ ]+++pSimplifier0+ :: (Ord n, Show n)+ => SimplifierDetails s a n+ -> Parser n (Simplifier s a n)++pSimplifier0 details+ = P.choice+ [ -- Fixpoint transform.+ -- fix INT SIMP+ do pTok KFix+ maxIters <- pInt+ simp <- pSimplifier0 details+ return $ Fix maxIters simp++ , do -- Atomic transform.+ trans <- pTransform details+ return $ Trans trans++ , do -- Simplifier in braces+ -- { SIMP }+ pTok KBraceBra+ simpl <- pSimplifierSeq details+ pTok KBraceKet+ return simpl+ ]+++-- | Parse a single transform.+pTransform + :: (Ord n, Show n)+ => SimplifierDetails s a n+ -> Parser n (Transform s a n)++pTransform details+ = P.choice+ [ -- Single transforms with no parameters.+ do trans <- P.pTokMaybe readTransformAtomic+ return trans++ -- Namifier+ , do pTok (KCon "Namify")+ return $ Namify (simplifierMkNamifierT details)+ (simplifierMkNamifierX details)++ -- Rewrite+ , do pTok (KCon "Rewrite")+ return $ Rewrite (simplifierRules details) ++ -- Inline+ , do pTok (KCon "Inline")+ let modules = simplifierTemplates details+ specs <- P.many pInlinerSpec+ let specsMap = Map.fromList specs+ return $ Inline (lookupTemplateFromModules specsMap modules) ]+++-- | Parse an inlining specification.+pInlinerSpec + :: Ord n+ => Parser n (ModuleName, InlineSpec n)++pInlinerSpec + = P.choice + [ do modname <- pModuleName+ P.choice+ [ pInlinerSpecIncludeList modname+ , pInlinerSpecExcludeList modname+ , return (modname, InlineSpecAll modname (Set.empty :: Set n)) ]+ ]++-- Inline all bindings in a module, except particulars.+-- Inline MODULENAME +[VAR1, VAR2, ... VARn]+-- Inline MODULENAME [VAR1, VAR2, ... VARn]+pInlinerSpecIncludeList modname+ = do P.choice [ pTok KPlus, return () ]+ pTok KSquareBra+ ns <- P.sepEndBy pVar (pTok KComma)+ pTok KSquareKet+ return $ (modname, InlineSpecNone modname (Set.fromList ns))+++-- Inline no bindings in a module by default,+-- but include some particulars.+-- Inline MODULENAME -[VAR1, VAR2, ... VARn]+pInlinerSpecExcludeList modname+ = do pTok KMinus+ pTok KSquareBra+ ns <- P.sepEndBy pVar (pTok KComma)+ pTok KSquareKet+ return $ (modname, InlineSpecAll modname (Set.fromList ns))+++-- | Read an atomic transform name.+readTransformAtomic :: Tok n -> Maybe (Transform s a n)+readTransformAtomic kk+ | KCon name <- kk+ = case name of+ "Id" -> Just Id+ "Anonymize" -> Just Anonymize+ "Beta" -> Just (Beta Beta.configZero)+ "BetaLets" -> Just (Beta Beta.configZero { Beta.configBindRedexes = True })+ "Bubble" -> Just Bubble+ "Elaborate" -> Just Elaborate+ "Eta" -> Just (Eta Eta.configZero { Eta.configExpand = True })+ "Flatten" -> Just Flatten+ "Forward" -> Just Forward+ "FoldCase" -> Just (FoldCase FoldCase.configZero+ { FoldCase.configCaseOfConstructor = True+ , FoldCase.configCaseOfCase = True })+ "Lambdas" -> Just Lambdas+ "Prune" -> Just Prune+ "Snip" -> Just (Snip Snip.configZero)+ "SnipOver" -> Just (Snip Snip.configZero { Snip.configSnipOverApplied = True })+ "SnipBody" -> Just (Snip Snip.configZero { Snip.configSnipLetBody = True })+ _ -> Nothing++ | otherwise+ = Nothing+++-- | Parse a variable name+pVar :: Parser n n+pVar = P.pTokMaybe f+ where f (KVar n) = Just n+ f _ = Nothing+++-- | Parse an integer.+pInt :: Parser n Int+pInt = P.pTokMaybe f+ where f (KInt i) = Just i+ f _ = Nothing+++-- | Parse a module name.+pModuleName :: Parser n ModuleName+pModuleName = P.pTokMaybe f+ where f (KCon n) = Just $ ModuleName [n]+ f _ = Nothing+
+ DDC/Core/Simplifier/Recipe.hs view
@@ -0,0 +1,118 @@++-- | Common simplifier recipes that combine multiple transforms.+module DDC.Core.Simplifier.Recipe+ ( -- * Atomic recipies+ idsimp+ , anonymize+ , beta+ , betaLets+ , bubble+ , elaborate+ , flatten+ , forward+ , lambdas+ , snip+ , snipOver+ , prune++ -- * Compound recipies+ , anormalize+ , rewriteSimp)+where+import DDC.Core.Simplifier.Base+import DDC.Core.Transform.Namify+import qualified DDC.Core.Transform.Snip as Snip+import qualified DDC.Core.Transform.Beta as Beta+import DDC.Type.Env+import Data.Monoid+++-- Atomic ---------------------------------------------------------------------+-- These are short names for single transforms.++-- | The identity simplifier returns the code unharmed.+idsimp :: Simplifier s a n+idsimp = Trans Id+++-- | Rewrite named binders to anonymous debruijn binders.+anonymize :: Simplifier s a n+anonymize = Trans Anonymize+++-- | Perform beta reduction+beta :: Simplifier s a n+beta = Trans (Beta Beta.configZero)+++-- | Perform beta reduction, introducing let-expressions for compound arguments.+betaLets :: Simplifier s a n+betaLets = Trans (Beta Beta.configZero { Beta.configBindRedexes = True })+++-- | Float casts outwards.+bubble :: Simplifier s a n+bubble = Trans Bubble+++-- | Elaborate possible Const and Distinct witnesses that aren't+-- otherwise in the program.+elaborate :: Simplifier s a n+elaborate = Trans Elaborate+++-- | Flatten nested let and case expressions.+flatten :: Simplifier s a n+flatten = Trans Flatten+++-- | Float single-use bindings forward into their use sites.+forward :: Simplifier s a n+forward = Trans Forward+++-- | Lift out nested lambda expressions to top-level.+lambdas :: Simplifier s a n+lambdas = Trans Lambdas+++-- | Remove unused, pure let bindings.+prune :: Simplifier s a n+prune = Trans Prune+++-- | Introduce let-bindings for nested applications.+snip :: Simplifier s a n+snip = Trans (Snip Snip.configZero)+++-- | Introduce let-bindings for nested applications.+snipOver :: Simplifier s a n+snipOver = Trans (Snip Snip.configZero { Snip.configSnipOverApplied = True })+++-- Compound -------------------------------------------------------------------+-- | Conversion to administrative normal-form.+anormalize + :: (KindEnv n -> Namifier s n) + -- ^ Make a namifier to create fresh level-1 names.+ -> (TypeEnv n -> Namifier s n) + -- ^ Make a namifier to create fresh level-0 names.+ -> Simplifier s a n++anormalize namK namT+ = snip+ <> Trans Flatten + <> Trans (Namify namK namT)+++-- | Intersperse rewrites and beta reduction+rewriteSimp+ :: Int -- ^ Maximum number of iterations.+ -> NamedRewriteRules a n -- ^ Rewrite rules to apply.+ -> Simplifier s a n++rewriteSimp maxIters rules+ = let rewrite = Trans $ Rewrite rules+ in Fix maxIters (rewrite <> bubble <> betaLets)+
+ DDC/Core/Simplifier/Result.hs view
@@ -0,0 +1,70 @@++module DDC.Core.Simplifier.Result+ ( TransformResult (..)+ , TransformInfo (..)+ , NoInformation+ , resultDone)+where+import DDC.Data.Pretty+import Data.Typeable+import qualified DDC.Data.Pretty as P+++-- TransformResult ------------------------------------------------------------+-- | Package up the result of applying a single transform.+data TransformResult r+ = TransformResult+ { -- | Transform result proper (eg the new module)+ result :: r++ -- | Whether this transform made any progess.+ -- + -- If `False` then the result program must be the same as the+ -- input program, and a simplifer fixpoint won't apply this+ -- transform again to the result program.+ , resultProgress :: Bool++ -- | Whether it might help to run the same transform again.+ -- + -- If `False` then a simplifier fixpoint won't apply this transform+ -- again to the result program.+ , resultAgain :: Bool++ -- | Transform specific log. This might contain a count of what rules+ -- fired, or information about what parts of the program couldn't+ -- be processed.+ , resultInfo :: TransformInfo }+++-- | Existential package for a typeable thing,+-- used in `TransformResult`.+data TransformInfo+ = forall i+ . (Typeable i, Pretty i)+ => TransformInfo i+++-- | Place-holder type to use when there is no real `TransformResult`.+data NoInformation + = NoInformation String+ deriving Typeable+++instance Pretty NoInformation where+ ppr (NoInformation name) = text name P.<> text ": No information"+++instance Pretty (TransformResult r) where+ ppr (TransformResult _ _ _ (TransformInfo i))+ = ppr i+++-- | Create a default result with no transform again.+-- +-- We'll say we made progress, but set `resultAgain` to False+-- so to stop any simplifier fixpoints.+resultDone :: String -> r -> TransformResult r+resultDone name r + = TransformResult r True False+ $ TransformInfo + $ NoInformation name
− DDC/Core/Transform/ANormal.hs
@@ -1,253 +0,0 @@--module DDC.Core.Transform.ANormal- (anormalise)-where-import DDC.Core.Exp-import qualified DDC.Type.Exp as T-import qualified DDC.Type.Compounds as T-import qualified DDC.Core.Transform.AnonymizeX as A-import qualified DDC.Core.Transform.LiftX as L--import qualified Data.Map as Map---- **** Recording arities of known values--- So we can try to create apps to fully apply ---- | Arities of known bound variables.--- We need to track everything even if it's not a function to keep indices correct.--- Just use zero for unknown/irrelevant-type Arities n = (Map.Map n Int, [Int])---- | Empty arities context-arEmpty :: Ord n => Arities n-arEmpty = (Map.empty, [])---- | Extend map with multiple bindings and their arities-arExtends :: Ord n => Arities n -> [(Bind n, Int)] -> Arities n-arExtends arity exts = foldl go arity exts- where go (named,anon) (BNone _t, _) = (named,anon)- go (named,anon) (BAnon _t, a) = (named, a:anon)- go (named,anon) (BName n _t, a) = (Map.insert n a named, anon)---- | Look up a binder's arity-arGet :: Ord n => Arities n -> Bound n -> Int-arGet (_named, anon) (UIx ix _) = anon !! ix-arGet (named, _anon) (UName n _) = named Map.! n--- Get a primitive's arity from its type-arGet (_named,_anon) (UPrim _ t) = arityOfType t---- **** Finding arities of expressions etc---- | Count all the arrows and foralls, ignoring any effects--- We can be sure that primitives don't effect until they're fully applied-arityOfType :: Ord n => Type n -> Int-arityOfType (T.TForall _ t)- = 1 + arityOfType t-arityOfType t- = let (args, _) = T.takeTFunArgResult t in- length args---- | Find arity of an expression. Count lambdas, use type for primitives-arityOfExp :: Ord n => Exp a n -> Int--- Counting all binders, because they all correspond to XApps.-arityOfExp (XLam _ _ e)- = 1 + arityOfExp e-arityOfExp (XLAM _ _ e)- = 1 + arityOfExp e--- Find primitive's constructor's arities from type,--- we might need to do this for user defined constructors too.-arityOfExp (XCon _ (UPrim _ t))- = arityOfType t--- Anything else we'll need to apply one at a time-arityOfExp _- = 0---- | Retrieve binders from case pattern, so we can extend the arity context.--- We don't know anything about their values, so record as 0.-aritiesOfPat :: Ord n => Pat n -> [(Bind n, Int)]-aritiesOfPat PDefault = []-aritiesOfPat (PData _b bs) = zip bs (repeat 0)----- **** Actually converting to a-normal form---- | Recursively transform expression into a-normal-anormal :: Ord n- => Arities n -- ^ environment, arities of bound variables- -> Exp a n -- ^ expression to transform- -> [(Exp a n,a)]-- ^ arguments being applied to current expression- -> Exp a n---- Application: just record argument and descend into function-anormal ar (XApp a lhs rhs) args- = -- normalise rhs and add to arguments- let args' = (anormal ar rhs [], a) : args in- -- descend into lhs, remembering all args- anormal ar lhs args'---- Anything other than application: if we're applied to arguments add bindings,--- otherwise just recurse.-anormal ar x args- = let x' = go x in- case args of- -- if there are no args, we're done- [] -> x'- -- there are arguments. we must apply them.- _ -> flattenLets $ makeLets ar x' args- where- -- helper for descent- down ars e = anormal (arExtends ar ars) e []-- -- we know x isn't an app.- go (XApp{}) = error "DDC.Core.Transform.ANormal.anormal: impossible XApp!"-- -- leafy ones- go (XVar{}) = x- go (XCon{}) = x- go (XType{}) = x- go (XWitness{}) = x-- -- lambdas- go (XLAM a b e) =- XLAM a b (down [(b,0)] e)- go (XLam a b e) =- XLam a b (down [(b,0)] e)-- -- non-recursive let- go (XLet a (LLet m b le) re) =- let le' = down [] le in- let re' = down [(b, arityOfExp le')] re in- XLet a (LLet m b le') re'-- -- recursive let- go (XLet a (LRec lets) re) =- let bs = map fst lets in- let es = map snd lets in- let ars= zip bs (map arityOfExp es) in- let es'= map (down ars) es in- let re'= down ars re in- XLet a (LRec $ zip bs es') re' -- -- letregion, just make sure we record bindings with dummy val- go (XLet a (LLetRegion b bs) re) =- let ars = zip bs (repeat 0) in- XLet a (LLetRegion b bs) (down ars re)-- -- withregion: I don't think this should ever show up.- go (XLet a (LWithRegion b) re) =- XLet a (LWithRegion b) (down [] re)-- -- case- go (XCase a e alts) =- let e' = down [] e in- let alts' = map (\(AAlt pat ae) -> AAlt pat (down (aritiesOfPat pat) ae)) alts in- XCase a e' alts'-- -- cast- go (XCast a c e) =- XCast a c (down [] e)----- | Convert an expression into a-normal form-anormalise :: Ord n => Exp a n -> Exp a n-anormalise x = anormal arEmpty x []---- | Check if an expression needs a binding, or if it's simple enough to be applied as-is-isNormal :: Ord n => Exp a n -> Bool--- Trivial expressions-isNormal (XVar{}) = True-isNormal (XCon{}) = True-isNormal (XType{}) = True-isNormal (XWitness{}) = True--- Casts are ignored by code generator, so we can leave them in if their subexpression is normal-isNormal (XCast _ _ x) = isNormal x-isNormal _ = False- --- | Create lets for any non-trivial arguments-makeLets :: Ord n- => Arities n -- ^ environment, arities of bound variables- -> Exp a n -- ^ function- -> [(Exp a n,a)]-- ^ arguments being applied to current expression- -> Exp a n-makeLets _ f0 [] = f0-makeLets ar f0 args@((_,annot):_) = go 0 (findArity f0) ((f0,annot):args) []- where- tBot = T.tBot T.kData-- -- out of arguments, create XApps out of leftovers- go i _arf [] acc = mkApps i 0 acc- -- f is fully applied and we have arguments left to add:- -- create let for intermediate result- go i arf ((x,a):xs) acc | length acc > arf- = XLet a (LLet LetStrict (BAnon tBot) (mkApps i 0 acc))- (go i 1 ((x,a):xs) [(XVar a $ UIx 0 tBot,a)])- -- application to variable, don't bother binding- go i arf ((x,a):xs) acc | isNormal x- = go i arf xs ((x,a):acc)- -- non-trivial argument, create binding- go i arf ((x,a):xs) acc- = XLet a (LLet LetStrict (BAnon tBot) (L.liftX i x))- (go (i+1) arf xs ((x,a):acc))- - -- fold list into applications- -- can't create empty app- mkApps _ _ []- = error "DDC.Core.Transform.ANormal.makeLets.mkApps: impossible empty list"-- -- single element - this is the function- mkApps l _ [(x,_)] | isNormal x- = L.liftX l x- mkApps _ i [(_,a)]- = XVar a $ UIx i tBot-- -- apply this argument and recurse- mkApps l i ((x,a):xs) | isNormal x- = XApp a (mkApps l i xs) (L.liftX l x)- mkApps l i ((_,a):xs)- = XApp a (mkApps l (i+1) xs) (XVar a $ UIx i tBot)-- findArity (XVar _ b) = max (arGet ar b) 1- findArity x = max (arityOfExp x) 1---- | Perform let-floating on strict non-recursive lets--- Only does the top level, to clean up the ones directly produced by makeLets.--- let b1 = (let b2 = def2 in x2)--- in x1--- ==>--- let b2 = def2--- in let b1 = x2--- in x1-flattenLets :: Ord n- => Exp a n- -> Exp a n---- We only do this if b2 is anonymous (ones generated by makeLets are).--- If we tried to wrap x1 in b2 when b2's name is already used,--- we'd be in trouble.-flattenLets- (XLet a1- (LLet LetStrict b1- (XLet a2 (LLet LetStrict b2@(BAnon _) def2) x2))- x1)- = -- If b1 is anon, we don't want to lift references to it- let liftDepth = case b1 of { BAnon _ -> 1; _ -> 0 } in- let x1' = L.liftAtDepthX 1 liftDepth x1 in- XLet a2 (LLet LetStrict b2 def2) $- flattenLets $ XLet a1 (LLet LetStrict b1 x2) x1'---- Same as above but b2 isn't anonymous - anonymize inner let & re-flatten.-flattenLets- (XLet a1- (LLet LetStrict b1 inner@(XLet _ (LLet LetStrict _ _) _))- x1)- = flattenLets $- XLet a1- (LLet LetStrict b1 (A.anonymizeX inner))- x1---- Any let, its bound expression doesn't contain a strict non-recursive let so just flatten the body-flattenLets (XLet a1 llet1 x1)- = XLet a1 llet1 (flattenLets x1)---- Anything else we can ignore. We don't need to recurse, because this is always called immediately after makeLets.-flattenLets x = x
DDC/Core/Transform/AnonymizeX.hs view
@@ -1,19 +1,23 @@ +-- | Rewrite all binders to anonymous deBruijn form. module DDC.Core.Transform.AnonymizeX ( anonymizeX- , AnonymizeX(..))+ , AnonymizeX(..)+ , pushAnonymizeBindX) where+import DDC.Core.Module import DDC.Core.Exp import DDC.Type.Transform.AnonymizeT-import DDC.Type.Compounds-import Control.Monad+import DDC.Type.Exp.Simple import Data.List+import Data.Set (Set)+import qualified Data.Set as Set --- | Rewrite all binders in a thing to be of anonymous form.+-- | Rewrite all binders in a thing to anonymous form. anonymizeX :: (Ord n, AnonymizeX c) => c n -> c n anonymizeX xx- = anonymizeWithX [] [] xx+ = anonymizeWithX Set.empty [] [] xx -------------------------------------------------------------------------------@@ -23,153 +27,175 @@ -- The stacks contains existing anonymous binders that we have entered into, -- and named binders that we have rewritten. All bound occurrences of variables -- will be replaced by references into these stacks.- anonymizeWithX - :: forall n. Ord n - => [Bind n] -- ^ Stack for Spec binders (level-1).+ anonymizeWithX+ :: forall n. Ord n+ => Set n -- ^ Don't anonymize level-0 binders with these names.+ -> [Bind n] -- ^ Stack for Spec binders (level-1). -> [Bind n] -- ^ Stack for Data and Witness binders (level-0). -> c n -> c n +instance AnonymizeX (Module a) where+ anonymizeWithX keep kstack tstack mm@ModuleCore{}+ = let+ -- We need to keep exported names,+ -- because the export list can't deal with anonymous binders.+ keep' = Set.union keep+ $ Set.fromList+ $ map fst $ moduleExportTypes mm + x' = anonymizeWithX keep' kstack tstack (moduleBody mm)++ in mm { moduleBody = x' }++ instance AnonymizeX (Exp a) where- anonymizeWithX kstack tstack xx- = let down = anonymizeWithX kstack tstack+ anonymizeWithX keep kstack tstack xx+ = {-# SCC anonymizeWithX #-}+ let down = anonymizeWithX keep kstack tstack in case xx of- XVar a u -> XVar a (down u)- XCon a u -> XCon a (down u)+ XVar _ UPrim{} -> xx+ XCon{} -> xx++ XVar a u@(UName{})+ | Just ix <- findIndex (boundMatchesBind u) tstack+ -> XVar a (UIx ix)++ XVar a u+ -> XVar a u+ XApp a x1 x2 -> XApp a (down x1) (down x2) XLAM a b x -> let (kstack', b') = pushAnonymizeBindT kstack b- in XLAM a b' (anonymizeWithX kstack' tstack x)+ in XLAM a b' (anonymizeWithX keep kstack' tstack x) XLam a b x- -> let (tstack', b') = pushAnonymizeBindX kstack tstack b- in XLam a b' (anonymizeWithX kstack tstack' x)+ -> let (tstack', b') = pushAnonymizeBindX keep kstack tstack b+ in XLam a b' (anonymizeWithX keep kstack tstack' x) XLet a lts x- -> let (kstack', tstack', lts') - = pushAnonymizeLets kstack tstack lts- in XLet a lts' (anonymizeWithX kstack' tstack' x)+ -> let (kstack', tstack', lts')+ = pushAnonymizeLets keep kstack tstack lts+ in XLet a lts' (anonymizeWithX keep kstack' tstack' x) - XCase a x alts -> XCase a (down x) (map down alts)- XCast a c x -> XCast a (down c) (down x)- XType t -> XType (anonymizeWithT kstack t)- XWitness w -> XWitness (down w)+ XCase a x alts -> XCase a (down x) (map down alts)+ XCast a c x -> XCast a (down c) (down x)+ XType a t -> XType a (anonymizeWithT kstack t)+ XWitness a w -> XWitness a (down w) -instance AnonymizeX Cast where- anonymizeWithX kstack tstack cc- = case cc of+instance AnonymizeX (Cast a) where+ anonymizeWithX keep kstack tstack cc+ = let down = anonymizeWithX keep kstack tstack+ in case cc of CastWeakenEffect eff -> CastWeakenEffect (anonymizeWithT kstack eff)- CastWeakenClosure clo -> CastWeakenClosure (anonymizeWithT kstack clo)- CastPurify w -> CastPurify (anonymizeWithX kstack tstack w)- CastForget w -> CastForget (anonymizeWithX kstack tstack w)---instance AnonymizeX LetMode where- anonymizeWithX kstack tstack lm- = case lm of- LetStrict -> lm- LetLazy mw -> LetLazy $ liftM (anonymizeWithX kstack tstack) mw+ CastPurify w -> CastPurify (down w)+ CastBox -> CastBox+ CastRun -> CastRun instance AnonymizeX (Alt a) where- anonymizeWithX kstack tstack alt- = case alt of+ anonymizeWithX keep kstack tstack alt+ = let down = anonymizeWithX keep kstack tstack+ in case alt of AAlt PDefault x- -> AAlt PDefault (anonymizeWithX kstack tstack x)+ -> AAlt PDefault (down x) AAlt (PData uCon bs) x- -> let (tstack', bs') = pushAnonymizeBindXs kstack tstack bs- x' = anonymizeWithX kstack tstack' x+ -> let (tstack', bs') = pushAnonymizeBindXs keep kstack tstack bs+ x' = anonymizeWithX keep kstack tstack' x in AAlt (PData uCon bs') x' -instance AnonymizeX Witness where- anonymizeWithX kstack tstack ww- = let down = anonymizeWithX kstack tstack +instance AnonymizeX (Witness a) where+ anonymizeWithX keep kstack tstack ww+ = let down = anonymizeWithX keep kstack tstack in case ww of- WVar u -> WVar (down u)- WCon c -> WCon c- WApp w1 w2 -> WApp (down w1) (down w2)- WJoin w1 w2 -> WJoin (down w1) (down w2)- WType t -> WType (anonymizeWithT kstack t)+ WVar a u@(UName _)+ | Just ix <- findIndex (boundMatchesBind u) tstack+ -> WVar a (UIx ix) + WVar a u -> WVar a u+ WCon a c -> WCon a c+ WApp a w1 w2 -> WApp a (down w1) (down w2)+ WType a t -> WType a (anonymizeWithT kstack t) + instance AnonymizeX Bind where- anonymizeWithX kstack _tstack bb+ anonymizeWithX _keep kstack _tstack bb = let t' = anonymizeWithT kstack $ typeOfBind bb- in replaceTypeOfBind t' bb ---instance AnonymizeX Bound where - anonymizeWithX kstack tstack bb- = case bb of- UName _ t- | Just ix <- findIndex (boundMatchesBind bb) tstack- -> UIx ix (anonymizeWithT kstack t)- - _ -> bb+ in replaceTypeOfBind t' bb -- Push ------------------------------------------------------------------------- Push a binding occurrence of a type variable on the stack, --- returning the anonyized binding occurrence and the new stack.-pushAnonymizeBindX - :: Ord n - => [Bind n] -- ^ Stack for Spec binders (kind environment)- -> [Bind n] -- ^ Stack for Value and Witness binders (type environment)- -> Bind n +-- | Push a binding occurrence of a level-0 on the stack,+-- returning the anonyized binding occurrence and the new stack.+pushAnonymizeBindX+ :: Ord n+ => Set n -- ^ Don't anonymize binders with these names.+ -> [Bind n] -- ^ Stack for Spec binders (level-1)+ -> [Bind n] -- ^ Stack for Value and Witness binders (level-0)+ -> Bind n -> ([Bind n], Bind n) -pushAnonymizeBindX kstack tstack b- = let b' = anonymizeWithX kstack tstack b+pushAnonymizeBindX keep kstack tstack b@(BName n _)+ | Set.member n keep+ = let b' = anonymizeWithX keep kstack tstack b+ in (tstack, b')++pushAnonymizeBindX keep kstack tstack b@BNone{}+ = let b' = anonymizeWithX keep kstack tstack b t' = typeOfBind b'+ in (tstack, BNone t')++pushAnonymizeBindX keep kstack tstack b+ = let b' = anonymizeWithX keep kstack tstack b+ t' = typeOfBind b' tstack' = b' : tstack in (tstack', BAnon t') --- Push a binding occurrence on the stack, --- returning the anonyized binding occurrence and the new stack.--- Used in the definition of `anonymize`.-pushAnonymizeBindXs - :: Ord n - => [Bind n] -- ^ Stack for Spec binders (kind environment)- -> [Bind n] -- ^ Stack for Value and Witness binders (type environment)- -> [Bind n] +-- | Push a binding occurrence on the stack,+-- returning the anonyized binding occurrence and the new stack.+-- Used in the definition of `anonymize`.+pushAnonymizeBindXs+ :: Ord n+ => Set n -- ^ Don't anonymize binders with these names.+ -> [Bind n] -- ^ Stack for Spec binders (level-1)+ -> [Bind n] -- ^ Stack for Value and Witness binders (level-0)+ -> [Bind n] -> ([Bind n], [Bind n]) -pushAnonymizeBindXs kstack tstack bs- = mapAccumL (\tstack' b -> pushAnonymizeBindX kstack tstack' b)- tstack bs+pushAnonymizeBindXs keep kstack tstack bs+ = mapAccumL+ (\tstack' b -> pushAnonymizeBindX keep kstack tstack' b)+ tstack bs -pushAnonymizeLets - :: Ord n - => [Bind n] -> [Bind n] - -> Lets a n +pushAnonymizeLets+ :: Ord n+ => Set n+ -> [Bind n]+ -> [Bind n]+ -> Lets a n -> ([Bind n], [Bind n], Lets a n) -pushAnonymizeLets kstack tstack lts+pushAnonymizeLets keep kstack tstack lts = case lts of- LLet mode b x- -> let mode' = anonymizeWithX kstack tstack mode- x' = anonymizeWithX kstack tstack x- (tstack', b') = pushAnonymizeBindX kstack tstack b- in (kstack, tstack', LLet mode' b' x')+ LLet b x+ -> let x' = anonymizeWithX keep kstack tstack x+ (tstack', b') = pushAnonymizeBindX keep kstack tstack b+ in (kstack, tstack', LLet b' x') - LRec bxs + LRec bxs -> let (bs, xs) = unzip bxs- (tstack', bs') = pushAnonymizeBindXs kstack tstack bs- xs' = map (anonymizeWithX kstack tstack') xs+ (tstack', bs') = pushAnonymizeBindXs keep kstack tstack bs+ xs' = map (anonymizeWithX keep kstack tstack') xs bxs' = zip bs' xs' in (kstack, tstack', LRec bxs') - LLetRegion b bs- -> let (kstack', b') = pushAnonymizeBindT kstack b- (tstack', bs') = pushAnonymizeBindXs kstack' tstack bs- in (kstack', tstack', LLetRegion b' bs')-- LWithRegion{}- -> (kstack, tstack, lts)+ LPrivate b mt bs+ -> let (kstack', b') = mapAccumL pushAnonymizeBindT kstack b+ (tstack', bs') = pushAnonymizeBindXs keep kstack' tstack bs+ in (kstack', tstack', LPrivate b' mt bs')
DDC/Core/Transform/Beta.hs view
@@ -1,40 +1,174 @@ +-- | Beta-reduce applications of a explicit lambda abstractions +-- to variables and values. module DDC.Core.Transform.Beta- (betaReduce)+ ( Config (..)+ , configZero+ , Info (..)+ , betaReduce) where-import DDC.Core.Exp-import DDC.Core.Transform.TransformX+import DDC.Core.Exp.Annot+import DDC.Core.Fragment+import DDC.Core.Transform.TransformUpX import DDC.Core.Transform.SubstituteTX import DDC.Core.Transform.SubstituteWX import DDC.Core.Transform.SubstituteXX-import DDC.Type.Env (Env)-import qualified DDC.Type.Env as Env+import DDC.Core.Simplifier.Result +import DDC.Data.Pretty+import DDC.Core.Env.EnvX (EnvX)+import Control.Monad.Writer (Writer, runWriter, tell)+import Data.Typeable (Typeable)+import Prelude hiding ((<$>))+import qualified DDC.Core.Env.EnvX as EnvX ++-------------------------------------------------------------------------------+data Config+ = Config+ { -- | If we find a lambda abstraction applied to a redex then let-bind+ -- the redex and substitute the new variable instead.+ configBindRedexes :: Bool }+ deriving Show+++-- | Empty beta configuration with all flags set to False.+configZero :: Config+configZero+ = Config+ { configBindRedexes = False }+++-------------------------------------------------------------------------------+-- | A summary of what the beta reduction transform did.+data Info+ = Info+ { -- | Number of type applications reduced.+ infoTypes :: Int++ -- | Number of witness applications reduced.+ , infoWits :: Int++ -- | Number of value applications reduced.+ , infoValues :: Int++ -- | Number of redexes let-bound.+ , infoValuesLetted :: Int++ -- | Number of applications that we couldn't reduce.+ , infoValuesSkipped :: Int }+ deriving Typeable+++instance Pretty Info where+ ppr (Info ty wit val lets skip)+ = text "Beta reduction:"+ <$> indent 4 (vcat+ [ text "Types: " <> int ty+ , text "Witnesses: " <> int wit+ , text "Values: " <> int val+ , text "Values letted: " <> int lets+ , text "Values skipped: " <> int skip ])+++instance Monoid Info where+ mempty = Info 0 0 0 0 0+ mappend (Info ty1 wit1 val1 lets1 skip1)+ (Info ty2 wit2 val2 lets2 skip2)+ = Info + (ty1 + ty2) (wit1 + wit2) (val1 + val2)+ (lets1 + lets2) (skip1 + skip2)+++------------------------------------------------------------------------------- -- | Beta-reduce applications of a explicit lambda abstractions -- to variables and values.-betaReduce :: Ord n => Exp a n -> Exp a n-betaReduce - = transformUpX betaReduce1 Env.empty Env.empty+--+-- If the flag is set then if we find a lambda abstraction that is applied+-- to a redex then let-bind the redex and substitute the new variable+-- instead.+betaReduce + :: forall (c :: * -> * -> *) a n + . (Ord n, TransformUpMX (Writer Info) c)+ => Profile n -- ^ Language profile.+ -> Config -- ^ Beta transform config.+ -> c a n -- ^ Thing to transform.+ -> TransformResult (c a n) +betaReduce profile config x+ = {-# SCC betaReduce #-}+ let (x', info) = runWriter+ $ transformUpMX (betaReduce1 profile config) EnvX.empty x -betaReduce1 :: Ord n => Env n -> Env n -> Exp a n -> Exp a n-betaReduce1 _ _ xx- = case xx of- XApp _ (XLAM _ b11 x12) (XType t2)- -> substituteTX b11 t2 x12+ -- Check if any actual work was performed+ progress + = case info of+ Info ty wit val lets' _+ -> (ty + wit + val + lets') > 0 - XApp _ (XLam _ b11 x12) (XWitness w2)- -> substituteWX b11 w2 x12+ in TransformResult+ { result = x'+ , resultAgain = progress+ , resultProgress = progress+ , resultInfo = TransformInfo info } - XApp _ (XLam _ b11 x12) x2- | canBetaSubstX x2 - -> substituteXX b11 x2 x12 +-- | Do a single beta reduction for this application.+--+-- To avoid duplicating work, we only reduce value applications when the+-- the argument is not a redex.+--+-- If needed, we also insert 'weakclo' to ensure the result has the same+-- closure as the original expression.+-- +betaReduce1+ :: Ord n+ => Profile n -- ^ Language profile.+ -> Config -- ^ Beta tranform config.+ -> EnvX n -- ^ Current environment+ -> Exp a n -- ^ Expression to transform.+ -> Writer Info (Exp a n)++betaReduce1 _profile config _env xx+ = let ret info x = tell info >> return x++ in case xx of++ -- Substitute type arguments into type abstractions.+ -- If the type argument of the redex does not appear as an + -- argument of the result then we need to add a closure weakening+ -- for the case where t2 was a region variable or handle.+ XApp _a (XLAM _ b11 x12) (XType _a2 t2)+ | isRegionKind $ typeOfBind b11+ -> ret mempty { infoTypes = 1}+ $ substituteTX b11 t2 x12++ -- Substitute type arguments into type abstractions,+ -- Where the argument is not a region type.+ XApp _a (XLAM _ b11 x12) (XType _ t2)+ -> ret mempty { infoTypes = 1 }+ $ substituteTX b11 t2 x12++ -- Substitute witness arguments into witness abstractions.+ XApp _a (XLam _ b11 x12) (XWitness _a2 w2)+ -> ret mempty { infoWits = 1 }+ $ substituteWX b11 w2 x12++ -- Substitute value arguments into value abstractions.+ XApp a (XLam _ b11 x12) x2+ | canBetaSubstX x2+ -> ret mempty { infoValues = 1 }+ $ substituteXX b11 x2 x12++ | configBindRedexes config+ -> ret mempty { infoValuesLetted = 1 }+ $ XLet a (LLet b11 x2) x12+ | otherwise- -> xx+ -> ret mempty { infoValuesSkipped = 1 }+ $ xx - _ -> xx+ _ -> return xx -- | Check whether we can safely substitute this expression during beta@@ -43,6 +177,7 @@ -- We allow variables, abstractions, type and witness applications. -- Duplicating these expressions is guaranteed not to duplicate work -- at runtime,+-- canBetaSubstX :: Exp a n -> Bool canBetaSubstX xx = case xx of@@ -51,10 +186,10 @@ XLam{} -> True XLAM{} -> True - XApp _ x1 (XType _)+ XApp _ x1 XType{} -> canBetaSubstX x1 - XApp _ x1 (XWitness _)+ XApp _ x1 XWitness{} -> canBetaSubstX x1 _ -> False
+ DDC/Core/Transform/Boxing.hs view
@@ -0,0 +1,390 @@+-- | Manage representation of numeric values in a module.+--+-- [Note: Boxing and Partial Application]+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+-- Unlike in Haskell, we do not allow explictly unboxed types in the source+-- program because we don't want to deal with partial applications of+-- functions to unboxed values. With our current setup we always have a version+-- of each function that accepts boxed values, so we never need to do generic+-- application involving unboxed values. Fast-path function specialisations+-- that take unboxed parameters should be created separately, and not replace+-- the existing slow-path, fully boxed version. Taking this approach is possible+-- in a strict language because the boxed and unboxed values have the same +-- semantic meaning. Boxing of values does not imply "lifting" of the associated+-- semantic domain.+--+module DDC.Core.Transform.Boxing+ ( Rep (..)+ , Config (..)+ , boxingModule)+where+import DDC.Core.Module+import DDC.Core.Exp.Annot+import DDC.Type.Transform.Instantiate+import Data.Maybe+++---------------------------------------------------------------------------------------------------+-- | Representation of the values of some type.+data Rep+ -- | These types don't contain any values.+ = RepNone++ -- | Values of this type are uncomitted to a particular representation,+ -- they just describe a set of logical values.+ | RepBoxed++ -- | Values of this type are represented in unboxed form.+ | RepUnboxed+ deriving (Eq, Ord, Show)+++data Config a n+ = Config+ { -- | Get the representation of this type.+ configRepOfType :: Type n -> Maybe Rep++ -- | Get the type for a different representation of the given one.+ , configConvertRepType :: Rep -> Type n -> Maybe (Type n)++ -- | Convert a value between representations.+ , configConvertRepExp :: Rep -> a -> Type n -> Exp a n -> Maybe (Exp a n) ++ -- | Take the type of a literal name, if there is one.+ , configValueTypeOfLitName :: n -> Maybe (Type n)++ -- | Take the type of a primitive operator name, if it is one.+ -- The primops can be polytypic, but must have prenex rank-1 types.+ , configValueTypeOfPrimOpName :: n -> Maybe (Type n) ++ -- | Take the type of a foreign function name, if it is one.+ -- The function can be polymorphic, but must have a prenex rank-1 type.+ , configValueTypeOfForeignName :: n -> Maybe (Type n)++ -- | Convert a literal name to its unboxed version.+ , configUnboxLitName :: n -> Maybe n++ -- | Covnert a primop name to its unboxed version.+ , configUnboxPrimOpName :: n -> Maybe n+ }+++-- Module -----------------------------------------------------------------------------------------+-- | Manage boxing in a module.+boxingModule + :: Ord n+ => Config a n -> Module a n -> Module a n++boxingModule config mm+ = let + -- Use explicitly unboxed types when importing foreign sea functions.+ boxingImport imp+ = case imp of+ ImportValueSea v t+ -> ImportValueSea v $ boxingForeignSeaType config t+ _ -> imp++ -- Use explicitly unboxed types when exporting foreign sea functions.+ nsImportSea = [ n | (n, ImportValueSea _ _) <- moduleImportValues mm]+ boxingExport expt+ = case expt of+ ExportSourceLocal n t+ | elem n nsImportSea+ -> ExportSourceLocal n $ boxingForeignSeaType config t+ _ -> expt++ in mm { moduleBody + = boxingX config (moduleBody mm)++ , moduleExportValues + = [(n, boxingExport expt) | (n, expt) <- moduleExportValues mm ]++ , moduleImportValues + = [(n, boxingImport impt) | (n, impt) <- moduleImportValues mm ] }+++boxingX config xx+ = case xx of++ -- Convert literals to their unboxed form, followed by a boxing conversion.+ XCon a (DaConPrim n tLit)+ | Just RepBoxed <- configRepOfType config tLit+ , Just tLitU <- configConvertRepType config RepUnboxed tLit+ , Just nU <- configUnboxLitName config n++ , Just xLit <- configConvertRepExp config RepBoxed a tLitU + $ XCon a (DaConPrim nU tLitU)+ -> xLit++ -- Use unboxed versions of primops by unboxing their arguments then + -- reboxing their results.+ XCast _ CastRun xx'@(XApp a _ _)+ | Just (n, xsArgsAll) <- takeXPrimApps xx'+ , Just n' <- configUnboxPrimOpName config n+ -> let Just tPrimBoxed = configValueTypeOfPrimOpName config n+ Just tPrimUnboxed = configValueTypeOfPrimOpName config n'+ xsArgsAll' = map (boxingX config) xsArgsAll+ in boxingPrimitive config a True xx' (XVar a (UPrim n' tPrimUnboxed)) + tPrimBoxed tPrimUnboxed+ xsArgsAll'++ -- Unbox primitive applications.+ XApp a _ _+ | Just (n, xsArgsAll) <- takeXPrimApps xx+ , Just n' <- configUnboxPrimOpName config n+ -> let Just tPrimBoxed = configValueTypeOfPrimOpName config n+ Just tPrimUnboxed = configValueTypeOfPrimOpName config n'+ xsArgsAll' = map (boxingX config) xsArgsAll+ in boxingPrimitive config a False xx (XVar a (UPrim n' tPrimUnboxed))+ tPrimBoxed tPrimUnboxed+ xsArgsAll'++ -- Foreign calls+ XApp a _ _+ | Just (xFn@(XVar _ (UName n)), xsArgsAll)+ <- takeXApps xx+ , Just tForeign <- configValueTypeOfForeignName config n+ -> let xsArgsAll' = map (boxingX config) xsArgsAll+ in boxingForeignSea config a xx xFn tForeign xsArgsAll'++ -- Unbox literal patterns in alternatives.+ XCase a xScrut alts+ | p : _ <- [ p | AAlt (PData p@DaConPrim{} []) _ <- alts]+ , Just tLit1 <- configValueTypeOfLitName config (daConName p)+ , Just RepBoxed <- configRepOfType config tLit1+ -> let alts' = map (boxingAlt config) alts+ in boxingCase config a tLit1 xScrut alts'++ -- Boilerplate.+ XVar{} -> xx+ XCon{} -> xx+ XLAM a b x -> XLAM a b (boxingX config x)+ XLam a b x -> XLam a b (boxingX config x)+ XApp a x1 x2 -> XApp a (boxingX config x1) (boxingX config x2)+ XLet a lts x -> XLet a (boxingLts config lts) (boxingX config x)+ XCase a x alts -> XCase a (boxingX config x) (map (boxingAlt config) alts)+ XCast a c x -> XCast a c (boxingX config x)+ XType{} -> xx+ XWitness{} -> xx++boxingLts config lts+ = case lts of+ LLet b x -> LLet b (boxingX config x)+ LRec bxs -> LRec [(b, boxingX config x) | (b, x) <- bxs]+ LPrivate{} -> lts++boxingAlt config alt+ = case alt of+ AAlt p x -> AAlt p (boxingX config x)+++---------------------------------------------------------------------------------------------------+-- | Marshall arguments and return values of primitive operations.+-- If something goes wrong then just return the original expression and leave it to+-- follow on transforms to report the error. The code generator won't be able to+-- convert the original expression.+--+-- * Assumes that the type of the primitive is in prenex form.+--+boxingPrimitive+ :: Ord n+ => Config a n -> a+ -> Bool -- ^ Primitive is being run at the call site.+ -> Exp a n -- ^ Whole primitive application, for debugging.+ -> Exp a n -- ^ Functional expression.+ -> Type n -- ^ Type of the boxed version of the primitive.+ -> Type n -- ^ Type of the unboxed version of the primitive.+ -> [Exp a n] -- ^ Arguments to the primitive.+ -> Exp a n++boxingPrimitive config a bRun xx xFn tPrimBoxed tPrimUnboxed xsArgsAll+ = fromMaybe xx go+ where+ go = do + -- Split off the type args.+ let (asArgs, tsArgs) = unzip [(a', t) | XType a' t <- xsArgsAll]+ let xsArgs = drop (length tsArgs) xsArgsAll++ -- Get the boxed version of the types of parameters and return value.+ tPrimBoxedInst <- instantiateTs tPrimBoxed tsArgs+ let (tsParamBoxed, _tResultBoxed) + = takeTFunArgResult tPrimBoxedInst++ -- Get the unboxed version of the types of parameters and return value.+ tPrimUnboxedInst <- instantiateTs tPrimUnboxed tsArgs+ let (_tsParamUnboxed, tResultUnboxed)+ = takeTFunArgResult tPrimUnboxedInst++ -- If the primitive is being run at the call site then we need to + -- re-box the result AFTER it has been run, not before.+ let tResultUnboxed'+ | not bRun = tResultUnboxed+ | otherwise = case takeTSusp tResultUnboxed of+ Just (_, t) -> t+ Nothing -> tResultUnboxed++ -- We must end up with a type of each argument.+ -- If not then the primop is partially applied or something else is wrong.+ -- The Tetra to Salt conversion will give a proper error message+ -- if the primop is indeed partially applied.+-- (if not ( length xsArgs == length tsParamBoxed+-- && length xsArgs == length tsParamUnboxed)+-- then Nothing+-- else Just ())++ -- We got a type for each argument, so the primop is fully applied+ -- and we can do the boxing/unboxing transform.+ let xsArgs' = [ (let t = fromMaybe xArg+ $ configConvertRepExp config RepUnboxed a tArgInst xArg + in t)+ | xArg <- xsArgs+ | tArgInst <- tsParamBoxed ]++ -- Construct the result expression, running it if necessary.+ let xtsArgsU = [ XType a' t | t <- tsArgs | a' <- asArgs ]+ let xResultU = xApps a xFn (xtsArgsU ++ xsArgs')+ let xResultRunU+ | not bRun = xResultU+ | otherwise = XCast a CastRun xResultU++ let xResultV = fromMaybe xResultRunU+ $ configConvertRepExp config RepBoxed a tResultUnboxed' xResultRunU++ return xResultV+++---------------------------------------------------------------------------------------------------+-- Marshall arguments and return values of foreign imported functions.+-- +-- * Assumes that the type of the imported thing is in prenex form.+--+boxingForeignSea+ :: Ord n+ => Config a n -> a + -> Exp a n -- ^ Whole function application, for debugging.+ -> Exp a n -- ^ Functional expression.+ -> Type n -- ^ Type of the foreign function.+ -> [Exp a n] -- ^ Arguments to the foreign function.+ -> Exp a n++boxingForeignSea config a xx xFn tF xsArg+ = fromMaybe xx go+ where go = do+ -- Split off the type args.+ let (_asArg, tsArgType) = unzip [(a', t) | XType a' t <- xsArg]+ let xsArgVal = drop (length tsArgType) xsArg++ -- Get the argument and return types of the function.+ -- Unlike primitives, foreign functions are not polytypic, so we can+ -- just erase any outer foralls to reveal the types of the args.+ let (tsArgVal, tResult) + = takeTFunArgResult+ $ eraseTForalls tF++ -- We must end up with a type for each argument.+ (if not (length xsArgVal == length tsArgVal)+ then Nothing+ else Just ())++ -- For each argument, if it has an unboxed representation then unbox it.+ let unboxArg xArg tArg + = fromMaybe xArg+ $ configConvertRepExp config RepUnboxed a tArg xArg++ let xsArgValU = zipWith unboxArg xsArgVal tsArgVal+ let xExpU = xApps a xFn ([XType a t | t <- tsArgType] ++ xsArgValU)++ -- If the result has a boxed representation then box it.+ let boxResult tRes xRes+ = fromMaybe xRes+ $ do tResU <- configConvertRepType config RepUnboxed tRes+ configConvertRepExp config RepBoxed a tResU xExpU++ return $ boxResult tResult xExpU+++-- | Marshall arguments and return values for function imported from Sea land.+boxingForeignSeaType+ :: Config a n -> Type n -> Type n++boxingForeignSeaType config tForeign+ = let + -- Split the type into quantifiers, parameter and result types.+ (bsForall, tBody) + = fromMaybe ([], tForeign)+ $ takeTForalls tForeign++ (tsParam, tResult) + = takeTFunArgResult tBody++ -- If there is an unboxed representation of each parameter and result+ -- type, then use that.+ unboxType tThing+ = fromMaybe tThing+ $ configConvertRepType config RepUnboxed tThing++ tsParamU = map unboxType tsParam+ tResultU = unboxType tResult++ -- Build the converted type back out of its parts.+ Just tBodyU = tFunOfList (tsParamU ++ [tResultU])+ tForeignU = foldr TForall tBodyU bsForall++ in tForeignU+++---------------------------------------------------------------------------------------------------+-- For case expressions that match against literals, like+--+-- case e1 of +-- { 5# -> e2; _ -> e3 }+--+-- Unbox the scrutinee and convert the alternatives to match against+-- unboxed literals.+-- +-- case convert# [Nat] [Nat#] e1 of+-- { 5## -> e2; _ -> e3 }+--+boxingCase + :: Config a n+ -> a -> Type n+ -> Exp a n+ -> [Alt a n]+ -> Exp a n++boxingCase config a tLit1 xScrut alts+ = let+ unboxAlt (AAlt (PData (DaConPrim n tLit) []) x)+ | Just RepBoxed <- configRepOfType config tLit+ , Just nU <- configUnboxLitName config n+ , Just tLitU <- configConvertRepType config RepUnboxed tLit+ = Just (AAlt (PData (DaConPrim nU tLitU) []) x)++ unboxAlt alt@(AAlt PDefault _) = Just alt+ unboxAlt _ = Nothing++ Just alts_unboxed+ = sequence $ map unboxAlt alts++ Just xScrut' = configConvertRepExp config RepUnboxed a tLit1 xScrut+ alts_default = ensureDefault alts_unboxed++ in XCase a xScrut' $ alts_default+++-- | Ensure that there is a default alternative in this list, +-- if not then make the last one the default.+-- We need do this to handle the case when the unboxed type does not have+-- all its constructors listed in the data defs. If it doesn't then the +-- case exhaustiveness checker will compilain when checking the result code.+ensureDefault :: [Alt a n] -> [Alt a n]+ensureDefault alts+ | _ : _ <- [alt | alt@(AAlt PDefault _) <- alts]+ = alts++ | AAlt (PData _ []) x : rest <- reverse alts+ = reverse rest ++ [AAlt PDefault x]++ | otherwise+ = alts+
+ DDC/Core/Transform/Bubble.hs view
@@ -0,0 +1,285 @@++-- | Bubble casts outwards.+-- We float casts up and outwards so they are just inside the inner-most+-- enclosing let. This way the functions still have the same effect and+-- closure, but the casts don't get in the way of subsequent transforms.+--+module DDC.Core.Transform.Bubble+ ( bubbleModule+ , bubbleX)+where+import DDC.Core.Collect+import DDC.Core.Transform.BoundX+import DDC.Core.Module+import DDC.Core.Exp.Annot+import DDC.Type.Env (KindEnv, TypeEnv)+import qualified DDC.Type.Env as Env+import qualified DDC.Type.Sum as Sum+import qualified Data.Set as Set+import Data.Set (Set)+import Data.List+++-- | Bubble casts outwards in a `Module`.+bubbleModule + :: Ord n+ => Module a n -> Module a n++bubbleModule mm@ModuleCore{}+ = let kenv = moduleKindEnv mm+ tenv = moduleTypeEnv mm+ in mm { moduleBody = bubbleX kenv tenv (moduleBody mm) }+++-- | Bubble casts outwards in an `Exp`.+bubbleX :: Ord n+ => KindEnv n -> TypeEnv n -> Exp a n -> Exp a n+bubbleX kenv tenv x+ = let + -- Bubble the expression, yielding any casts that floated out+ -- to top-level.+ (cs, x') = bubble kenv tenv x+ a = annotOfExp x'++ -- Reattach top-level casts.+ in dropAllCasts kenv tenv a cs x'+++-------------------------------------------------------------------------------+-- | Bubble casts outwards in some thing.+class Bubble (c :: * -> * -> *) where+ bubble :: Ord n+ => KindEnv n+ -> TypeEnv n+ -> c a n + -> ([FvsCast a n], c a n)+++instance Bubble Exp where+ bubble kenv tenv xx+ = {-# SCC bubble #-}+ case xx of+ XVar{} -> ([], xx)+ XCon{} -> ([], xx)++ -- Drop casts before we leave lambda abstractions, because the+ -- function type depends on the effect and closure of the body.+ -- The cast could also reference the bound variable.+ XLAM a b x+ -> let kenv' = Env.extend b kenv+ (cs, x') = bubble kenv' tenv x+ in ([], XLAM a b (dropAllCasts kenv' tenv a cs x'))++ XLam a b x+ -> let tenv' = Env.extend b tenv+ (cs, x') = bubble kenv tenv' x+ in ([], XLam a b (dropAllCasts kenv tenv' a cs x'))++ -- Decend into applications.+ XApp a x1 x2+ -> let (cs1, x1') = bubble kenv tenv x1+ (cs2, x2') = bubble kenv tenv x2+ in (cs1 ++ cs2, XApp a x1' x2')++ -- After decending into let-expressions, make sure to drop+ -- any casts that mention variables bound here.+ XLet a lts x2+ -> let (cs1, lts') = bubble kenv tenv lts+ (bs1, bs0) = bindsOfLets lts+ kenv' = Env.extends bs1 kenv+ tenv' = Env.extends bs0 tenv+ (cs2, x2') = bubble kenv' tenv' x2+ (cs2', x2'') = dropCasts kenv' tenv' a bs1 bs0 cs2 x2'+ in ( cs1 ++ cs2'+ , XLet a lts' x2'')++ -- Decent into case-expressions.+ -- Casts that depend on bound variables are dropped + -- in the corresponding alternatives.+ XCase a x alts+ -> let (cs, x') = bubble kenv tenv x+ (css, alts') = unzip $ map (bubble kenv tenv) alts+ in ( cs ++ concat css+ , XCase a x' alts')++ -- Strip of cast and pass it up.+ XCast _ c x+ -> let (cs, x') = bubble kenv tenv x+ fvsT = freeT Env.empty c+ fvsX = freeX Env.empty c+ fc = FvsCast c fvsT fvsX+ in (fc : cs, x')++ XType{} -> ([], xx)+ XWitness{} -> ([], xx)+++instance Bubble Lets where+ bubble kenv tenv lts+ = case lts of++ -- Drop casts that mention the bound variable here, + -- but we can float the others further outwards.+ LLet b x+ -> let (cs, x') = bubble kenv tenv x+ a = annotOfExp x'+ (cs', xc') = dropCasts kenv tenv a [] [b] cs x'+ in (cs', LLet b xc')++ -- ISSUE #299: Bubble casts out of recursive lets.+ LRec bxs+ -> let bs = map fst bxs+ tenv' = Env.extends bs tenv++ bubbleRec (b, x)+ = let (cs, x') = bubble kenv tenv' x+ a = annotOfExp x'+ in (b, dropAllCasts kenv tenv' a cs x')++ bxs' = map bubbleRec bxs++ in ([], LRec bxs')++ LPrivate{}+ -> ([], lts)+++instance Bubble Alt where++ -- Default patterns don't bind variables, + -- so there is no problem floating casts outwards.+ bubble kenv tenv (AAlt PDefault x)+ = let (cs, x') = bubble kenv tenv x+ in (cs, AAlt PDefault x')++ -- Drop casts before we leave the alt because they could contain+ -- variables bound by the pattern.+ bubble kenv tenv (AAlt p x)+ = let bs = bindsOfPat p+ a = annotOfExp x'+ tenv' = Env.extends bs tenv+ (cs, x') = bubble kenv tenv' x+ (csUp, xcHere) = dropCasts kenv tenv' a [] bs cs x'+ in (csUp, AAlt p xcHere)+++-- FvsCast --------------------------------------------------------------------+-- | A Cast along with its free level-1 and level-0 vars.+-- When we first build a `FvsCast` we record its free variables, +-- so that we don't have to keep recomputing them.+data FvsCast a n+ = FvsCast (Cast a n)+ (Set (Bound n)) -- Free level-1 variables.+ (Set (Bound n)) -- Free level-0 variables.++instance Ord n => MapBoundX (FvsCast a) n where+ mapBoundAtDepthX f d (FvsCast c fvs1 fvs0)+ = FvsCast (mapBoundAtDepthX f d c)+ fvs1+ (Set.fromList+ $ map (mapBoundAtDepthX f d)+ $ Set.toList fvs0)+++packFvsCasts + :: Ord n+ => KindEnv n -> TypeEnv n+ -> a -> [FvsCast a n] -> [Cast a n]++packFvsCasts kenv tenv a fvsCasts+ = packCasts kenv tenv a [ c | FvsCast c _ _ <- fvsCasts ]+++-- | Pack down casts by combining multiple 'weakclo' and 'weakeff' casts+-- together. We pack casts just before we drop them, so that the resulting+-- code is easier to read.+packCasts :: Ord n+ => KindEnv n -> TypeEnv n -> a -> [Cast a n] -> [Cast a n]+packCasts _kenv _tenv _a vs+ = let + -- Sort casts into weakeff, weakclo and any others.+ -- We'll collect the weakeff and weakclo casts together.+ collect weakEffs weakClos others cc+ = case cc of+ [] + -> (reverse weakEffs, reverse weakClos, reverse others)++ CastWeakenEffect eff : cs + -> collect (eff : weakEffs) weakClos others cs++ c : cs+ -> collect weakEffs weakClos (c : others) cs+++ (effs, csOthers, _) + = collect [] [] [] vs++ in (if null effs + then []+ else [CastWeakenEffect (TSum $ Sum.fromList kEffect effs)])+ ++ csOthers+++-- Dropping -------------------------------------------------------------------+-- | Wrap the provided expression with these casts.+dropAllCasts + :: Ord n+ => KindEnv n+ -> TypeEnv n+ -> a + -> [FvsCast a n] -> Exp a n + -> Exp a n++dropAllCasts kenv tenv a cs x+ = let cs' = packFvsCasts kenv tenv a cs+ in foldr (XCast a) x cs'+++-- | Split the provided casts into ones that contain variables+-- bound by these binders. The casts that do are used to wrap+-- the provided expression, and the casts that don't are returned+-- seprately so we can keep bubbling them up the tree.+dropCasts + :: Ord n+ => KindEnv n -> TypeEnv n+ -> a + -> [Bind n] -- ^ Level-1 binders.+ -> [Bind n] -- ^ Level-0 binders.+ -> [FvsCast a n] + -> Exp a n + -> ([FvsCast a n], Exp a n)++dropCasts kenv tenv a bs1 bs0 cs x+ = let (csHere1, cs1) = partition (fvsCastUsesBinds1 bs1) cs+ (csHere0, csUp) = partition (fvsCastUsesBinds0 bs0) cs1+ csHere = packFvsCasts kenv tenv a $ csHere1 ++ csHere0+ in ( map (lowerX 1) csUp+ , foldr (XCast a) x csHere)+++-- | Check if a `FvsCast` mentions any of these level-0 variables.+fvsCastUsesBinds0 :: Ord n => [Bind n] -> FvsCast a n -> Bool+fvsCastUsesBinds0 bb (FvsCast _ _ fvs0)+ = bindsMatchBoundSet bb fvs0+++-- | Check if a `FvsCast` mentions any of these level-1 variables.+fvsCastUsesBinds1 :: Ord n => [Bind n] -> FvsCast a n -> Bool+fvsCastUsesBinds1 bb (FvsCast _ fvs1 _)+ = bindsMatchBoundSet bb fvs1+++-- | Check if a set of bound variables matches any of the given binders.+bindsMatchBoundSet :: Ord n => [Bind n] -> Set (Bound n) -> Bool+bindsMatchBoundSet bb fvs+ = go bb+ where go [] = False+ go (b : bs)+ | Just u <- takeSubstBoundOfBind b+ = if Set.member u fvs+ then True+ else go bs++ | otherwise+ = go bs+
+ DDC/Core/Transform/Elaborate.hs view
@@ -0,0 +1,110 @@++-- | Add possible Const and Distinct witnesses that aren't+-- otherwise in the program.+module DDC.Core.Transform.Elaborate+ ( elaborateModule+ , elaborateX )+where+import DDC.Core.Exp+import DDC.Core.Module+import DDC.Type.Exp.Simple+import DDC.Data.ListUtils+import Control.Monad+import Control.Arrow+import Data.Maybe+import Data.List+++-- | Elaborate witnesses in a module.+elaborateModule :: Eq n => Module a n -> Module a n+elaborateModule mm + = mm { moduleBody = elaborate [] $ moduleBody mm }+++-- | Elaborate witnesses in an expression.+elaborateX :: Eq n => Exp a n -> Exp a n+elaborateX xx+ = elaborate [] xx+++-------------------------------------------------------------------------------+class Elaborate (c :: * -> *) where+ elaborate :: Eq n => [Bound n] -> c n -> c n+++instance Elaborate (Exp a) where+ elaborate us xx+ = {-# SCC elaborate #-}+ let down = elaborate us + in case xx of+ XVar{} -> xx+ XCon{} -> xx + XLAM a b x -> XLAM a b (down x)+ XLam a b x -> XLam a b (down x)+ XApp a x1 x2 -> XApp a (down x1) (down x2)++ XLet a lts x2 + -> let (us', lts') = elaborateLets us lts+ in XLet a lts' (elaborate us' x2)+ + XCase a x alts -> XCase a (down x) (map down alts)+ XCast a cst x2 -> XCast a (down cst) (down x2)+ XType{} -> xx+ XWitness{} -> xx+++instance Elaborate (Cast a) where+ elaborate _us cst = cst+ ++instance Elaborate (Alt a) where+ elaborate us (AAlt p x) = AAlt p (elaborate us x) +++-- | Elaborate witnesses in some let-bindings.+elaborateLets+ :: Eq n + => [Bound n] -- ^ Witness bindings in the environment.+ -> Lets a n -- ^ Elaborate these let bindings.+ -> ([Bound n], Lets a n)++elaborateLets us lts + = let down = elaborate us + in case lts of+ LLet b x -> (us, LLet b (down x))+ LRec bs -> (us, LRec $ map (second down) bs)++ LPrivate brs mt bws+ | urs@(_:_) <- takeSubstBoundsOfBinds brs+ -> let + -- Mutable regions bound here.+ rsMutable = catMaybes + $ map (takeMutableRegion . typeOfBind) bws++ -- Make a new const witness for all non-mutable regions.+ constWits = map makeConstWit + $ urs \\ rsMutable++ -- Make a new distinct witness against all regions+ -- in the environment.+ Just ursTail = takeTail urs+ distinctWits = map makeDistinctWit + $ liftM2 (,) us urs+ ++ zip urs ursTail++ in ( us ++ urs+ , LPrivate brs mt $ bws ++ distinctWits ++ constWits )++ _ -> (us, lts)++makeConstWit u+ = BNone $ tConst (TVar u) ++makeDistinctWit (u1,u2)+ = BNone $ tDistinct 2 [TVar u1, TVar u2]++takeMutableRegion tt+ = case takeTyConApps tt of+ Just (TyConWitness TwConMutable, [TVar u]) -> Just u+ _ -> Nothing+
+ DDC/Core/Transform/Eta.hs view
@@ -0,0 +1,306 @@++-- Eta-expand functional values.+---+-- NOTE: This is module currently just does eta-expansion, but in future+-- we should expand the config to also make it do expansion/contraction+-- based on the real arity of bindings.+--+module DDC.Core.Transform.Eta+ ( Config(..)+ , configZero+ , Info (..)+ , etaModule+ , etaX)+where+import qualified DDC.Core.Check as Check+import DDC.Core.Module+import DDC.Core.Exp.Annot+import DDC.Core.Fragment+import DDC.Core.Transform.BoundX+import DDC.Core.Transform.BoundT+import DDC.Core.Simplifier.Result+import DDC.Core.Pretty+import DDC.Type.Transform.AnonymizeT+import Control.Monad.Writer (Writer, tell, runWriter)+import Data.Typeable+import DDC.Core.Env.EnvX (EnvX)+import qualified DDC.Core.Env.EnvX as EnvX+import Prelude hiding ((<$>))+++-------------------------------------------------------------------------------+data Config+ = Config+ { configExpand :: Bool }+ deriving Show+++-- | Empty eta configuration with all flags set to False.+configZero :: Config+configZero+ = Config+ { configExpand = False }+++-------------------------------------------------------------------------------+data Info+ = Info+ { -- | Number of level-1 lambdas added.+ infoExpandedXLAMs :: Int ++ -- | Number of level-0 lambdas added. + , infoExpandedXLams :: Int }+ deriving Typeable+++instance Pretty Info where+ ppr (Info ex1 ex0)+ = text "Eta Transform"+ <$> indent 4 (vcat+ [ text "level-1 lambdas added: " <> int ex1 + , text "level-0 lambdas added: " <> int ex0 ])+++instance Monoid Info where+ mempty = Info 0 0+ mappend (Info ex1 ex0)+ (Info ex1' ex0')+ = Info (ex1 + ex1') (ex0 + ex0')+++-- Module ---------------------------------------------------------------------+-- | Eta-transform expressions in a module.+etaModule+ :: (Ord n, Show n, Pretty n, Show a)+ => Profile n+ -> Config+ -> Module a n+ -> TransformResult (Module a n)++etaModule profile config mm+ = let + -- Slurp type checker config.+ cconfig = Check.configOfProfile profile++ -- Slurp the top level environment.+ env = moduleEnvX+ (profilePrimKinds profile)+ (profilePrimTypes profile)+ (profilePrimDataDefs profile)+ mm+ + -- Run the eta transform.+ (mm', info) = runWriter $ etaM config cconfig env mm+ + -- Check if any actual work was performed+ progress+ = case info of+ Info ex1 ex0+ -> ex1 + ex0 > 0++ in TransformResult+ { result = mm'+ , resultAgain = False+ , resultProgress = progress+ , resultInfo = TransformInfo info }+++-- | Eta-transform an expression.+etaX :: (Ord n, Show n, Show a, Pretty n)+ => Profile n -- ^ Language profile.+ -> Config -- ^ Eta-transform config.+ -> EnvX n -- ^ Type checker environment.+ -> Exp a n -- ^ Expression to transform.+ -> TransformResult (Exp a n)++etaX profile config env xx+ = let cconfig = Check.configOfProfile profile++ -- Run the eta transform.+ (xx', info) + = runWriter+ $ etaM config cconfig env xx++ -- Check if any actual work was performed+ progress+ = case info of+ Info ex1 ex0+ -> ex1 + ex0 > 0++ in TransformResult+ { result = xx'+ , resultAgain = False+ , resultProgress = progress+ , resultInfo = TransformInfo info }+++-------------------------------------------------------------------------------+class Eta (c :: * -> * -> *) where+ etaM :: (Show a, Ord n, Pretty n, Show n)+ => Config -- ^ Eta-transform config.+ -> Check.Config n -- ^ Type checker config.+ -> EnvX n -- ^ Type checker environment.+ -> c a n -- ^ Do eta-expansion in this thing.+ -> Writer Info (c a n)+++instance Eta Module where+ etaM config cconfig envx mm+ = do -- The top level environment of the module is added+ -- by the etaModule wrapper, so we don't need to do it again.+ xx' <- etaM config cconfig envx (moduleBody mm)+ return $ mm { moduleBody = xx' }+++instance Eta Exp where+ etaM config cconfig env xx+ = let down = etaM config cconfig env+ in case xx of++ XVar a _+ | configExpand config+ , Right tX <- Check.typeOfExp cconfig env xx+ -> do etaExpand a tX xx++ XApp a _ _+ | configExpand config+ , Right tX <- Check.typeOfExp cconfig env xx+ -> do + -- Decend into the arguments first.+ -- We don't need to decend into the function part because+ -- we're eta-expanding that here.+ let (x : xs) = takeXAppsAsList xx+ xs_eta <- mapM down xs++ -- Now eta expand the result.+ etaExpand a tX $ xApps a x xs_eta++ XLAM a b x+ -> do let env' = EnvX.extendT b env+ x' <- etaM config cconfig env' x+ return $ XLAM a b x'++ XLam a b x+ -> do let env' = EnvX.extendX b env+ x' <- etaM config cconfig env' x+ return $ XLam a b x'++ XLet a lts x2+ -> do lts' <- down lts+ let (bs1, bs0) = bindsOfLets lts++ let env' = EnvX.extendsT bs1 + $ EnvX.extendsX bs0 env++ x2' <- etaM config cconfig env' x2+ return $ XLet a lts' x2'++ XCase a x alts+ -> do x' <- down x+ alts' <- mapM (etaM config cconfig env) alts+ return $ XCase a x' alts'++ XCast a cc x+ -> do x' <- down x+ return $ XCast a cc x'++ _ -> return xx+++instance Eta Lets where+ etaM config cconfig env lts+ = let down = etaM config cconfig env+ in case lts of+ LLet b x+ -> do x' <- down x+ return $ LLet b x'++ LRec bxs+ -> do let bs = map fst bxs+ let env' = EnvX.extendsX bs env+ xs' <- mapM (etaM config cconfig env') + $ map snd bxs+ return $ LRec (zip bs xs')++ LPrivate{}+ -> return lts+++instance Eta Alt where+ etaM config cconfig env alt+ = case alt of+ AAlt p x + -> do let bs = bindsOfPat p+ let env' = EnvX.extendsX bs env+ x' <- etaM config cconfig env' x+ return $ AAlt p x'+++-- Expand ---------------------------------------------------------------------+-- | Eta expand an expression.+etaExpand + :: Ord n+ => a -- ^ Annotation to use for new AST nodes.+ -> Type n -- ^ Type of the expression.+ -> Exp a n -- ^ Inner expression to wrap.+ -> Writer Info (Exp a n)++etaExpand a tX xx+ -- Anonymize the type, so any references to foralls will become anonymous.+ -- Then, when we add the anonymous bindings, it will work out.+ = do let btsMore = expandableArgs $ anonymizeT tX+ xx' <- etaExpand' a 0 0 [] btsMore xx+ return xx'+++-- | Decide what type arguments need to be eta-expanded.+expandableArgs :: Type n -> [(Bool, Type n)]+expandableArgs tt+ | TForall b t' <- tt+ = (True, typeOfBind b) : expandableArgs t'++ | Just (t1, t2) <- takeTFun tt+ = (False, t1) : expandableArgs t2++ | otherwise+ = []+++-- | Eta-expand an expression.+etaExpand'+ :: Ord n + => a -- ^ Annotation to use for the new AST nodes.+ -> Int -- ^ Number of level-1 lambdas we've added so far.+ -> Int -- ^ Number of level-0 lambdas we've added so far.+ -> [Exp a n] -- ^ Accumulate arguments we need to add to the+ -- inner expression.+ -> [(Bool, Type n)] -- ^ Types of bindings we need to add, along with+ -- a flag to indicate level-1 or level-0 binder+ -> Exp a n -- ^ Inner expression that is being applied.+ -> Writer Info (Exp a n)++etaExpand' a levels1 levels0 args [] xx+ = do let xx' = liftT levels1 $ liftX levels0 xx+ return $ xApps a xx' args++etaExpand' a levels1 levels0 args ((True, t) : ts) xx+ = do let depth1 = length $ filter ((== True) . fst) ts+ xx' <- etaExpand' a (levels1 + 1) levels0 + (args ++ [XType a (TVar (UIx depth1))]) + ts+ xx++ tell mempty { infoExpandedXLAMs = 1 }+ return $ XLAM a (BAnon t) xx'++etaExpand' a levels1 levels0 args ((False, t) : ts) xx+ = do let depth0 = length $ filter ((== False) . fst) ts+ xx' <- etaExpand' a + levels1 (levels0 + 1) + (args ++ [XVar a (UIx depth0)])+ ts+ xx++ tell mempty { infoExpandedXLams = 1 }+ return $ XLam a (BAnon t) xx'+
+ DDC/Core/Transform/Flatten.hs view
@@ -0,0 +1,134 @@++-- | Flattening nested let and case expressions.+module DDC.Core.Transform.Flatten+ (flatten)+where+import DDC.Core.Transform.TransformUpX+import DDC.Core.Transform.AnonymizeX+import DDC.Core.Transform.BoundX+import DDC.Core.Exp.Annot+import Data.Functor.Identity+++-- | Flatten binding structure in a thing.+--+-- Flattens nested let-expressions, +-- and single alternative let-case expressions. +--+flatten :: Ord n + => (TransformUpMX Identity c)+ => c a n -> c a n+flatten + = {-# SCC flatten #-}+ transformUpX' flatten1+++-- | Flatten a single nested let-expression.+flatten1+ :: Ord n+ => Exp a n+ -> Exp a n++-- Run ----------------------------------------------------+flatten1 (XCast a1 CastRun (XLet a2 lts x2))+ = XLet a2 lts $ flatten1 (XCast a1 CastRun x2)+++-- Let ----------------------------------------------------+-- Special case when nested let is (let b = x in b):+-- @+-- let b1 = (let ^ = def2 in ^0) in+-- x1+--+-- ==> let b1 = def2 in +-- x1+-- @+-- +flatten1 (XLet a1 (LLet b1+ (XLet _ (LLet (BAnon _) def2) (XVar _ (UIx 0))))+ x1)+ = flatten1+ $ XLet a1 (LLet b1 def2)+ x1++-- Let ----------------------------------------------------+-- Flatten Nested Lets.+-- @+-- let b1 = (let b2 = def2 in x2) in+-- x1+--+-- ==> let b2 = def2 in +-- let b1 = x2 in+-- x1+-- @+-- +flatten1 (XLet a1 (LLet b1+ inner@(XLet a2 (LLet b2 def2) x2))+ x1)++ | isBName b2+ = flatten1+ $ XLet a1 (LLet b1 + (anonymizeX inner))+ x1++ | otherwise+ = let x1' = liftAcrossX [b1] [b2] x1 + in XLet a2 (LLet b2 def2) + $ flatten1+ $ XLet a1 (LLet b1 x2) + x1'+++-- Flatten single-alt case expressions.+-- @+-- let b1 = case x1 of +-- P -> x2 +-- in x3+--+-- => case x1 of +-- P -> let b1 = x2 +-- in x3+-- @+--+-- * binding must be strict because we force evaluation of x1.+--+flatten1 (XLet a1 (LLet b1 + inner@(XCase a2 x1 [AAlt p x2]))+ x3)+ | any isBName $ bindsOfPat p+ = flatten1+ $ XLet a1 (LLet b1+ (anonymizeX inner))+ x3++ | otherwise+ = let x3' = liftAcrossX [b1] (bindsOfPat p) x3+ in XCase a2 x1 + [AAlt p ( flatten1 + $ XLet a1 (LLet b1 x2)+ (anonymizeX x3'))]+++-- Any let, its bound expression doesn't contain a strict non-recursive+-- let so just flatten the body+flatten1 (XLet a1 llet1 x1)+ = XLet a1 llet1 (flatten1 x1)+++-- Case ---------------------------------------------------+-- Flatten all the alternatives in a case-expression.+flatten1 (XCase a x1 alts)+ = XCase a (flatten1 x1) + [AAlt p (flatten1 x) | AAlt p x <- alts ]++flatten1 x = x+++liftAcrossX :: [Bind n] -> [Bind n] -> Exp a n -> Exp a n+liftAcrossX bsDepth bsLevels x+ = let depth = length [b | b@(BAnon _) <- bsDepth]+ levels = length [b | b@(BAnon _) <- bsLevels]+ in liftAtDepthX levels depth x++
+ DDC/Core/Transform/FoldCase.hs view
@@ -0,0 +1,84 @@++module DDC.Core.Transform.FoldCase+ ( Config (..)+ , configZero+ , foldCase+ , foldCaseX )+where+import DDC.Core.Exp.Annot+import DDC.Core.Transform.TransformDownX+import Control.Monad.State.Strict+import qualified Data.Map.Strict as M+import qualified DDC.Type.Env as Env++data Config+ = Config+ { -- | Perform the case-of-constructor transformation.+ configCaseOfConstructor :: Bool++ -- | Perform the case-of-case transformation.+ -- Not implemented yet.+ , configCaseOfCase :: Bool }++configZero :: Config+configZero+ = Config+ { configCaseOfConstructor = False+ , configCaseOfCase = False }+++---------------------------------------------------------------------------------------------------+type FoldCase a n = State (M.Map n (DaCon n (Type n), [Exp a n]))++foldCase :: (Ord n, TransformDownMX (FoldCase a n) c)+ => Config+ -> c a n+ -> c a n++foldCase config xx+ = {-# SCC foldCase #-}+ evalState (transformDownMX (\_ _ -> foldCaseX config) Env.empty Env.empty xx) M.empty++foldCaseX :: Ord n+ => Config+ -> Exp a n+ -> FoldCase a n (Exp a n)+++-- Collect ----------------------------------------------------------------------------------------+foldCaseX _+ x@(XLet _ (LLet (BName b _) ex) _)+ | Just (dc, args) <- takeXConApps ex+ = do+ modify (M.insert b (dc, args))+ return x+++-- Case of Constructor ----------------------------------------------------------------------------+-- @+-- let x = Con y z in+-- case x of+-- Con a b -> x1+--+-- ==> let x = Con y z in+-- let a = y in+-- let b = z in+-- x1+-- @+--+foldCaseX config+ x@(XCase _ (XVar _ (UName n)) [alt])+ | configCaseOfConstructor config+ , AAlt (PData dc binds) rest <- alt+ = do+ seen <- gets (M.lookup n)+ return $ case seen of+ Just (dc', args') | dc == dc'+ -> foldr (\(x', bnd) next -> XLet (annotOfExp x') (LLet bnd x') next)+ rest (zip (filter (not . isXType) args') binds)++ _ -> x+++-- Default case.+foldCaseX _ x = return x
+ DDC/Core/Transform/Forward.hs view
@@ -0,0 +1,302 @@++-- | Float let-bindings with a single use forward into their use-sites.+module DDC.Core.Transform.Forward+ ( ForwardInfo (..)+ , FloatControl (..)+ , Config(..)+ , forwardModule+ , forwardX)+where+import DDC.Data.Pretty+import DDC.Core.Analysis.Usage+import DDC.Core.Exp.Annot+import DDC.Core.Module+import DDC.Core.Simplifier.Base+import DDC.Core.Transform.Reannotate+import DDC.Core.Fragment+import Data.Map (Map)+import Control.Monad+import Control.Monad.Writer (Writer, runWriter, tell)+import Data.Typeable+import qualified Data.Map as Map+import qualified DDC.Core.Transform.SubstituteXX as S+import Prelude hiding ((<$>))+++-------------------------------------------------------------------------------+-- | Summary of number of bindings floated.+data ForwardInfo+ = ForwardInfo+ { -- | Number of bindings inspected.+ infoInspected :: !Int++ -- | Number of trivial @v1 = v2@ bindings inlined.+ , infoSubsts :: !Int++ -- | Number of bindings floated forwards.+ , infoBindings :: !Int }+ deriving Typeable+++instance Pretty ForwardInfo where+ ppr (ForwardInfo inspected substs bindings)+ = text "Forward:"+ <$> indent 4 (vcat+ [ text "Total bindings inspected: " <> int inspected+ , text " Trivial substitutions made: " <> int substs+ , text " Bindings moved forward: " <> int bindings ])+++instance Monoid ForwardInfo where+ mempty = ForwardInfo 0 0 0+ mappend (ForwardInfo i1 s1 b1)(ForwardInfo i2 s2 b2)+ = ForwardInfo (i1 + i2) (s1 + s2) (b1 + b2)+++-------------------------------------------------------------------------------+-- | Fine control over what should be floated.+data FloatControl+ = FloatAllow -- ^ Allow binding to be floated, but don't require it.+ | FloatDeny -- ^ Prevent a binding being floated, at all times.+ | FloatForce -- ^ Force a binding to be floated, at all times.+ | FloatForceUsedOnce -- ^ Force a binding to be floated if it's only used once.+ deriving (Eq, Show)++data Config a n+ = Config+ { configFloatControl :: Lets a n -> FloatControl+ , configFloatLetBody :: Bool }++-------------------------------------------------------------------------------+-- | Float let-bindings in a module with a single use forward into+-- their use sites.+forwardModule + :: Ord n+ => Profile n -- ^ Language profile+ -> Config a n+ -> Module a n + -> TransformResult (Module a n)++forwardModule profile config mm+ = let (mm', info)+ = runWriter+ $ forwardWith profile config Map.empty + $ usageModule mm++ progress (ForwardInfo _ s f)+ = s + f > 0++ in TransformResult+ { result = mm'+ , resultProgress = progress info+ , resultAgain = False+ , resultInfo = TransformInfo info }+++-- | Float let-bindings in an expression with a single use forward into+-- their use-sites.+forwardX :: Ord n+ => Profile n -- ^ Language profile.+ -> Config a n + -> Exp a n + -> TransformResult (Exp a n)++forwardX profile config xx+ = let (x',info) = runWriter+ $ forwardWith profile config Map.empty+ $ usageX xx++ progress (ForwardInfo _ s f) + = s + f > 0++ in TransformResult+ { result = x'+ , resultProgress = progress info+ , resultAgain = False+ , resultInfo = TransformInfo info }+++-------------------------------------------------------------------------------+class Forward (c :: * -> * -> *) where+ -- | Carry bindings forward and downward into their use-sites.+ forwardWith + :: Ord n+ => Profile n -- ^ Language profile.+ -> Config a n+ -> Map n (Exp a n) -- ^ Bindings currently being carried forward.+ -> c (UsedMap n, a) n+ -> Writer ForwardInfo (c a n)++instance Forward Module where+ forwardWith profile config bindings + (ModuleCore+ { moduleName = name+ , moduleIsHeader = isHeader+ , moduleExportTypes = exportTypes+ , moduleExportValues = exportValues+ , moduleImportTypes = importTypes+ , moduleImportCaps = importCaps+ , moduleImportValues = importValues+ , moduleImportDataDefs = importDataDefs+ , moduleImportTypeDefs = importTypeDefs+ , moduleDataDefsLocal = dataDefsLocal+ , moduleTypeDefsLocal = typeDefsLocal+ , moduleBody = body })++ = do body' <- forwardWith profile config bindings body+ return ModuleCore+ { moduleName = name+ , moduleIsHeader = isHeader+ , moduleExportTypes = exportTypes+ , moduleExportValues = exportValues+ , moduleImportTypes = importTypes+ , moduleImportCaps = importCaps+ , moduleImportValues = importValues+ , moduleImportDataDefs = importDataDefs+ , moduleImportTypeDefs = importTypeDefs+ , moduleDataDefsLocal = dataDefsLocal+ , moduleTypeDefsLocal = typeDefsLocal+ , moduleBody = body' }+++instance Forward Exp where+ forwardWith profile config bindings xx+ = {-# SCC forwardWith #-}+ let down = forwardWith profile config bindings + in case xx of+ XVar a u@(UName n)+ -> case Map.lookup n bindings of+ Just xx' -> do+ tell mempty { infoSubsts = 1 }+ return xx'+ Nothing ->+ return $ XVar (snd a) u++ XVar a u -> return $ XVar (snd a) u+ XCon a u -> return $ XCon (snd a) u+ XLAM a b x -> liftM (XLAM (snd a) b) (down x)+ XLam a b x -> liftM (XLam (snd a) b) (down x)+ XApp a x1 x2 -> liftM2 (XApp (snd a)) (down x1) (down x2)++ -- Always float last let-binding into its use.+ -- let x = exp in x => exp+ XLet _ (LLet b x1) (XVar _ u)+ | boundMatchesBind u b+ , configFloatLetBody config+ -> down x1++ -- A special case for atomic anonymous bindings.+ -- Always float atomic bindings (variables, constructors),+ -- but only if they're still atomic after forwarding them:+ -- if x1 is a variable to be replaced with a function, then+ -- substituting x1 into x2 could duplicate that.+ XLet (_, a) (LLet b@(BAnon _) x1) x2+ | isAtomX x1+ -> do+ x1' <- down x1+ if isAtomX x1'+ then do+ -- Record that we've moved this binding.+ tell mempty { infoInspected = 1+ , infoBindings = 1 }++ -- Slower, but handles anonymous binders and shadowing+ down $ S.substituteXX b x1 x2++ else do+ tell mempty { infoInspected = 1}+ liftM (XLet a $ LLet b x1') (down x2)++ XLet (UsedMap um, a') lts@(LLet (BName n t) x1) x2+ -> do + let control = configFloatControl config + $ reannotate snd lts++ let isFun = isXLam x1 || isXLAM x1++ let isApplied+ | Just usage <- Map.lookup n um+ , [UsedFunction] <- filterUsedInCasts usage+ = True+ | otherwise = False++ let shouldFloat+ = case control of+ FloatDeny -> False+ FloatForce -> True+ FloatAllow -> isFun && isApplied++ FloatForceUsedOnce+ | Just usage <- Map.lookup n um+ , length usage == 1+ -> True+ | otherwise+ -> False++ -- Always float atomic bindings (variables, constructors).+ x1' <- down x1++ if shouldFloat || isAtomX x1'+ then do+ -- Record that we've moved this binding.+ tell mempty { infoInspected = 1+ , infoBindings = 1 }++ let bindings' = Map.insert n x1' bindings+ forwardWith profile config bindings' x2++ else do + tell mempty { infoInspected = 1}++ -- Note that @n@ has been shadowed+ let bindings' = Map.delete n bindings+ x2' <- forwardWith profile config bindings' x2++ return $ XLet a' (LLet (BName n t) x1') x2'++ XLet (_, a') lts x + -> liftM2 (XLet a') (down lts) (down x)++ XCase a x alts -> liftM2 (XCase (snd a)) (down x) (mapM down alts)+ XCast a c x -> liftM2 (XCast (snd a)) (down c) (down x)+ XType a t -> return (XType (snd a) t)+ XWitness a w -> return (XWitness (snd a) (reannotate snd w))+++filterUsedInCasts :: [Used] -> [Used]+filterUsedInCasts = filter notCast+ where notCast UsedInCast = False+ notCast _ = True+++instance Forward Cast where+ forwardWith _profile _config _bindings xx+ = case xx of+ CastWeakenEffect eff -> return $ CastWeakenEffect eff+ CastPurify w -> return $ CastPurify (reannotate snd w)+ CastBox -> return $ CastBox+ CastRun -> return $ CastRun+++instance Forward Lets where+ forwardWith profile config bindings lts+ = let down = forwardWith profile config bindings+ in case lts of+ LLet b x + -> liftM (LLet b) (down x)++ LRec bxs + -> liftM LRec+ $ mapM (\(b,x) + -> do x' <- down x+ return (b, x')) + bxs++ LPrivate b mt bs+ -> return $ LPrivate b mt bs+++instance Forward Alt where+ forwardWith profile config bindings (AAlt p x)+ = liftM (AAlt p) (forwardWith profile config bindings x)+
+ DDC/Core/Transform/Inline.hs view
@@ -0,0 +1,75 @@++-- | Inlining definitions into their use sites.+module DDC.Core.Transform.Inline+ ( inline+ , InlineSpec (..)+ , lookupTemplateFromModules)+where+import DDC.Core.Exp+import DDC.Core.Module+import DDC.Core.Transform.Inline.Templates+import qualified Data.Set as Set+import Data.Set (Set)+++class Inline (c :: * -> * -> *) where+ inline :: Ord n+ => (n -> Maybe (Exp a n))+ -- ^ Get the template for a named variable.+ -> Set n -- ^ Don't inline definitions for these names.+ -> c a n+ -> c a n+++instance Inline Module where+ inline get inside mm+ = mm { moduleBody = inline get inside (moduleBody mm) }+++instance Inline Exp where+ inline get inside xx+ = let down x = inline get inside x+ in case xx of+ XVar _ (UName n)+ -- Don't inline a recursive definition into itself.+ | Set.member n inside+ -> xx++ -- If there is a template for this variable then inline it, + -- but remember that we're now inside the body so we don't inline+ -- recursive functions forever.+ | Just xx' <- get n+ -> let !inside' = Set.insert n inside+ in inline get inside' xx'++ XVar{} -> xx+ XCon{} -> xx+ XLAM a b x -> XLAM a b (down x)+ XLam a b x -> XLam a b (down x)+ XApp a x1 x2 -> XApp a (down x1) (down x2)+ XLet a lts x2 -> XLet a (down lts) (down x2)+ XCase a x alts -> XCase a (down x) (map down alts)+ XCast a c x -> XCast a c (down x)+ XType{} -> xx+ XWitness{} -> xx+++instance Inline Lets where+ inline get inside lts+ = let enter b x+ = case b of+ BName n _ -> inline get (Set.insert n inside) x+ _ -> inline get inside x++ in case lts of+ LLet b x -> LLet b (enter b x)+ LRec bxs -> LRec [(b, enter b x) | (b, x) <- bxs]+ LPrivate{} -> lts+++instance Inline Alt where+ inline get inside alt+ = case alt of+ AAlt p x -> AAlt p (inline get inside x)++
+ DDC/Core/Transform/Inline/Templates.hs view
@@ -0,0 +1,96 @@++-- | Retrieving inliner templates from a list of modules.+module DDC.Core.Transform.Inline.Templates+ ( InlineSpec(..)+ , lookupTemplateFromModules+ , lookupTemplateFromModule )+where+import DDC.Core.Exp+import DDC.Core.Module+import DDC.Core.Transform.AnonymizeX+import Data.List+import Data.Set (Set)+import Data.Map (Map)+import qualified Data.Map as Map+import qualified Data.Set as Set+++-- | Inlining specification says what bindings we should inline+-- from a particular module.+data InlineSpec n+ -- | Inline all bindings from a module,+ -- but exclude some particulars.+ = InlineSpecAll+ { inlineSpecModuleName :: ModuleName+ , inlineSpecExclude :: Set n }++ -- | Inline no bindings from a module,+ -- but include some particulars.+ | InlineSpecNone+ { inlineSpecModuleName :: ModuleName+ , inlineSpecInclude :: Set n }+ deriving Show+++-- | Lookup an inliner template from a list of modules.+---+-- This just does a linear search through all the modules.+-- As we only inline functions defined at top level, we don't need to worry+-- about lifting indices in templates when we go under binders.+--+lookupTemplateFromModules + :: (Ord n, Show n)+ => Map ModuleName (InlineSpec n)+ -- ^ Inliner specifications for the modules.+ -> [Module a n] -- ^ Modules to use for inliner templates.+ -> n + -> Maybe (Exp a n)++lookupTemplateFromModules specs mm n+ | m : ms <- mm+ = let -- If there is no inliner spec then don't inline anything.+ spec = case Map.lookup (moduleName m) specs of+ Just s -> s+ Nothing -> InlineSpecNone (moduleName m) Set.empty++ in case lookupTemplateFromModule spec m n of+ Nothing -> lookupTemplateFromModules specs ms n+ Just x -> Just x++ | otherwise+ = Nothing+++lookupTemplateFromModule + :: Ord n+ => InlineSpec n -- ^ Inliner specification for this module.+ -> Module a n -- ^ Module to use for inliner templates.+ -> n + -> Maybe (Exp a n)++lookupTemplateFromModule spec mm n+ | shouldInline spec n+ , XLet _ (LRec bxs) _ <- moduleBody mm+ , Just (_,x) <- find (\(BName n' _, _) -> n == n') bxs+ = Just $ anonymizeX x++ | otherwise+ = Nothing+++-- | Decide whether we should inline the binding with this name based on the +-- provided inliner specification.+shouldInline+ :: Ord n+ => InlineSpec n -> n -> Bool++shouldInline spec n+ = case spec of+ InlineSpecAll _ except+ | Set.member n except -> False+ | otherwise -> True++ InlineSpecNone _ include+ | Set.member n include -> True+ | otherwise -> False+
+ DDC/Core/Transform/Lambdas.hs view
@@ -0,0 +1,481 @@++module DDC.Core.Transform.Lambdas+ ( lambdasModule+ , evalState+ , newVar)+where+import DDC.Core.Transform.Lambdas.Lift+import DDC.Core.Transform.Lambdas.Base+import DDC.Core.Fragment+import DDC.Core.Collect.Support+import DDC.Core.Transform.SubstituteXX+import DDC.Core.Module+import DDC.Core.Exp.Annot.Context+import DDC.Core.Exp.Annot.Ctx+import DDC.Core.Exp.Annot+import DDC.Type.Transform.SubstituteT+import DDC.Data.Pretty+import DDC.Data.Name+import qualified DDC.Type.Env as Env+import qualified DDC.Type.DataDef as DataDef+import qualified DDC.Core.Env.EnvX as EnvX+import qualified Data.Set as Set+import qualified Data.Map as Map+import Data.Maybe+++---------------------------------------------------------------------------------------------------+-- | Perform lambda lifting in a module.+lambdasModule + :: ( Show a, Pretty a+ , Show n, Pretty n, Ord n, CompoundName n)+ => Profile n+ -> Module a n + -> S (Module a n)++lambdasModule profile mm+ = do+ -- Take the top-level environment of the module.+ let env = moduleEnvX + (profilePrimKinds profile)+ (profilePrimTypes profile)+ (profilePrimDataDefs profile)+ mm++ let c = Context env (CtxTop env)+ x' <- lambdasLoopX profile c $ moduleBody mm++ return + $ mm { moduleBody = x' }+++-- Exp --------------------------------------------------------------------------------------------+lambdasLoopX+ :: (Show n, Show a, Pretty n, Pretty a, CompoundName n, Ord n)+ => Profile n -- ^ Language profile.+ -> Context a n -- ^ Enclosing context.+ -> Exp a n -- ^ Expression to perform lambda lifting on.+ -> S (Exp a n) -- Replacement expression.+ +lambdasLoopX p c xx+ = do (xx1, Result progress _)+ <- lambdasTopX p c xx++ if progress + then lambdasLoopX p c xx1+ else return xx1++-- | Handle the expression that defines the body of the module+-- separately. The body is a letrec that contains the top level+-- bindings, and we don't want to lift them out further.+lambdasTopX p c xx+ = case xx of+ XLet a lts xBody+ -> do (lts', r1) <- lambdasTopLets p c a xBody lts + (x', r2) <- enterLetBody c a lts xBody (lambdasTopX p)+ return ( foldr (XLet a) x' lts'+ , mappend r1 r2)++ _ -> lambdasX p c xx+++-- | Handle the top-level group of bindings.+lambdasTopLets p c a xBody lts+ = case lts of+ LRec bxs+ -> do (bxs', r) <- lambdasLetRec p c a [] bxs xBody+ return ([LRec bxs'], r)++ _ -> lambdasLets p c a xBody lts+++---------------------------------------------------------------------------------------------------+-- | Perform a single pass of lambda lifting in an expression.+lambdasX :: (Show n, Show a, Pretty n, Pretty a, CompoundName n, Ord n)+ => Profile n -- ^ Language profile.+ -> Context a n -- ^ Enclosing context.+ -> Exp a n -- ^ Expression to perform lambda lifting on.+ -> S ( Exp a n -- Replacement expression+ , Result a n) -- Lifter result.+ +lambdasX p c xx+ = case xx of+ XVar{} + -> return (xx, mempty)++ XCon a (DaConBound nCon) + -> do mResult <- etaXConApp p c a xx nCon []+ case mResult of+ Nothing -> return (xx, mempty)+ Just result -> return result++ XCon{}+ -> return (xx, mempty)+ + -- Lift type lambdas to top-level.+ XLAM a b x0+ -> enterLAM c a b x0 $ \c' x+ -> do (x', r) <- lambdasX p c' x+ let xx' = XLAM a b x'+ let Result _ bxs = r+ + -- Decide whether to lift this lambda to top-level.+ -- If there are multiple nested lambdas then we want to lift+ -- the whole group at once, rather than lifting each one + -- individually.+ let liftMe = isLiftyContext (contextCtx c) && null bxs+ if liftMe+ then do+ let us' = supportEnvFlags+ $ support Env.empty Env.empty xx'+ + (xCall, bLifted, xLifted)+ <- liftLambda p c us' a [(True, b)] x'++ return ( xCall+ , Result True (bxs ++ [(bLifted, xLifted)]))++ else return (xx', r)+++ -- Lift value lambdas to top-level.+ XLam a b x0+ -> enterLam c a b x0 $ \c' x+ -> do (x', r) <- lambdasX p c' x+ let xx' = XLam a b x'+ let Result _ bxs = r+ + -- Decide whether to lift this lambda to top-level.+ let liftMe = isLiftyContext (contextCtx c) && null bxs++ if liftMe+ then do+ let us' = supportEnvFlags+ $ support Env.empty Env.empty xx'++ (xCall, bLifted, xLifted)+ <- liftLambda p c us' a [(False, b)] x'+ + return ( xCall+ , Result True (bxs ++ [(bLifted, xLifted)]))++ else return (xx', r)+++ -- Lift suspensions to top-level.+ -- These behave like zero-arity lambda expressions,+ -- they suspend evaluation but do not abstract over a value.+ XCast a cc@CastBox x0+ -> enterCastBody c a cc x0 $ \c' x+ -> do (x', r) <- lambdasX p c' x+ let xx' = XCast a CastBox x'+ let Result _ bxs = r++ -- Decide whether to lift this box to top-level.+ let liftMe = isLiftyContext (contextCtx c) && null bxs++ if liftMe + then do+ let us' = supportEnvFlags+ $ support Env.empty Env.empty xx'++ (xCall, bLifted, xLifted)+ <- liftLambda p c us' a [] (XCast a CastBox x')++ return ( xCall+ , Result True (bxs ++ [(bLifted, xLifted)]))++ else return (xx', r)+++ -- Eta-expand partially applied data contructors.+ XApp a x1 x2+ | (XCon _a (DaConBound nCon), xsArg) <- takeXApps1 x1 x2+ -> do+ mResult <- etaXConApp p c a x1 nCon xsArg+ case mResult of+ Just result + -> return result++ _ -> do (x1', r1) <- enterAppLeft c a x1 x2 (lambdasX p)+ (x2', r2) <- enterAppRight c a x1 x2 (lambdasX p)+ return ( XApp a x1' x2'+ , mappend r1 r2)+++ -- Boilerplate.+ XApp a x1 x2+ -> do (x1', r1) <- enterAppLeft c a x1 x2 (lambdasX p)+ (x2', r2) <- enterAppRight c a x1 x2 (lambdasX p)+ return ( XApp a x1' x2'+ , mappend r1 r2)+ + XLet a lts x+ -> do (lts', r1) <- lambdasLets p c a x lts + (x', r2) <- enterLetBody c a lts x (lambdasX p)+ return ( foldr (XLet a) x' lts'+ , mappend r1 r2)+ + XCase a x alts+ -> do (x', r1) <- enterCaseScrut c a x alts (lambdasX p)+ (alts', r2) <- lambdasAlts p c a x [] alts+ return ( XCase a x' alts'+ , mappend r1 r2)++ XCast a cc x+ -> lambdasCast p c a cc x+ + XType{} + -> return (xx, mempty)++ XWitness{} + -> return (xx, mempty)+++-- Lets -------------------------------------------------------------------------------------------+-- | Perform lambda lifting in some let-bindings.+lambdasLets+ :: (Show a, Show n, Ord n, Pretty n, Pretty a, CompoundName n)+ => Profile n -> Context a n+ -> a -> Exp a n+ -> Lets a n+ -> S ([Lets a n], Result a n)+ +lambdasLets p c a xBody lts+ = case lts of+ LLet b x+ -> do (x', r) <- enterLetLLet c a b x xBody (lambdasX p)+ return ([LLet b x'], r)++ LRec bxs+ -> do (bxs', r) <- lambdasLetRecLiftAll p c a bxs+ return (map (uncurry LLet) bxs', r)++ LPrivate{}+ -> return ([lts], mempty)+++-- LetRec -----------------------------------------------------------------------------------------+-- | Perform lambda lifting in the right of a single let-rec binding.+lambdasLetRec + :: (Show a, Show n, Ord n, Pretty n, Pretty a, CompoundName n)+ => Profile n -> Context a n+ -> a -> [(Bind n, Exp a n)] -> [(Bind n, Exp a n)] -> Exp a n+ -> S ([(Bind n, Exp a n)], Result a n)++lambdasLetRec _ _ _ _ [] _+ = return ([], mempty)++lambdasLetRec p c a bxsAcc ((b, x) : bxsMore) xBody+ = do (x', r1) + <- enterLetLRec c a bxsAcc b x bxsMore xBody (lambdasX p)++ case contextCtx c of++ -- If we're at top-level then drop lifted bindings here.+ CtxTop{}+ -> do (bxs', Result p2 bxs2) + <- lambdasLetRec p c a ((b, x') : bxsAcc) bxsMore xBody+ let Result p1 bxsLifted = r1+ return ( bxsLifted ++ ((b, x') : bxs')+ , Result (p1 || p2) bxs2 )++ _+ -> do (bxs', r2) + <- lambdasLetRec p c a ((b, x') : bxsAcc) bxsMore xBody+ return ( (b, x') : bxs'+ , mappend r1 r2 )+++-- | When all the bindings in a letrec are lambdas, lift them all together.+lambdasLetRecLiftAll+ :: (Show a, Show n, Ord n, Pretty n, Pretty a, CompoundName n)+ => Profile n -> Context a n+ -> a+ -> [(Bind n, Exp a n)]+ -> S ([(Bind n, Exp a n)], Result a n)++lambdasLetRecLiftAll p c a bxs+ = let -- Wrap the context in the letrec+ ctx before b x after+ = enterLetLRec c a before b x after x (\c' _ -> c')+ in do++ -- The union of free variables of all the mutually recursive bindings must be used,+ -- as any lifted function may call the other lifted functions.+ let us = Set.unions+ $ map (supportEnvFlags . support Env.empty Env.empty)+ $ map snd bxs++ -- However, the functions we are lifting should not be treated as free variables+ let us' = Set.filter + (\(_, bo) -> not $ any (boundMatchesBind bo . fst) bxs)+ $ us++ -- Lift each of the bindings in its own context.+ -- We allow the group of bindings to contain ones with outer lambda+ -- abstractions that need to be lifted, along with non-functional+ -- bindings that stay in place.+ let lift _before [] + = return []++ lift before ((b, x) : after)+ = case takeXLamFlags x of+ -- The right of this binding has an outer abstraction, + -- so we need to lift it to top-level.+ Just (lams, xx)+ -> do let c' = ctx before b x after+ l' <- liftLambda p c' us' a lams xx+ ls <- lift (before ++ [(b,x)]) after+ return $ Right (b, l') : ls++ -- The right of this binding does not have any outer+ -- abstractions, so we can leave it in place.+ Nothing+ -> do ls <- lift (before ++ [(b, x)]) after+ return $ Left (b, x) : ls++ ls <- lift [] bxs++ -- The call to each lifted function+ let stripCall (Left (b, x)) = (b, x)+ stripCall (Right (b, (xC, _, _))) = (b, xC)+ let calls = map stripCall ls++ -- Substitute the original name of the recursive function with a call to its new name,+ -- including passing along any free variables.+ -- Here, we need to unwrap the newly-created lambdas for the free variables,+ -- as capture-avoiding substitution would rename them - the opposite of what we want.+ let sub x = case takeXLamFlags x of+ Just (lams, xx) -> makeXLamFlags a lams (substituteXXs calls xx)+ Nothing -> substituteXXs calls x++ -- The result bindings to add at the top-level, with all the new names substituted in+ let stripResult (Left _) = Nothing+ stripResult (Right (_, (_, bL, xL))) = Just (bL, sub xL)++ let res = mapMaybe stripResult ls++ return (calls, Result True res)++++-- Alts -------------------------------------------------------------------------------------------+-- | Perform lambda lifting in the right of a single alternative.+lambdasAlts + :: (Show a, Show n, Ord n, Pretty n, Pretty a, CompoundName n)+ => Profile n -> Context a n+ -> a -> Exp a n -> [Alt a n] -> [Alt a n]+ -> S ([Alt a n], Result a n)+ +lambdasAlts _ _ _ _ _ []+ = return ([], mempty)++lambdasAlts p c a xScrut altsAcc (AAlt w x : altsMore)+ = do (x', r1) <- enterCaseAlt c a xScrut altsAcc w x altsMore (lambdasX p)+ (alts', r2) <- lambdasAlts p c a xScrut (AAlt w x' : altsAcc) altsMore+ return ( AAlt w x' : alts'+ , mappend r1 r2)+++-- Cast -------------------------------------------------------------------------------------------+-- | Perform lambda lifting in the body of a Cast expression.+lambdasCast+ :: (Show a, Show n, Ord n, Pretty n, Pretty a, CompoundName n)+ => Profile n -> Context a n+ -> a -> Cast a n -> Exp a n+ -> S (Exp a n, Result a n)++lambdasCast p c a cc x+ = case cc of+ CastWeakenEffect{} + -> do (x', r) <- enterCastBody c a cc x (lambdasX p)+ return ( XCast a cc x', r)++ CastPurify{}+ -> do (x', r) <- enterCastBody c a cc x (lambdasX p)+ return (XCast a cc x', r)++ CastBox + -> do (x', r) <- enterCastBody c a cc x (lambdasX p)+ return (XCast a cc x', r)+ + CastRun + -> do (x', r) <- enterCastBody c a cc x (lambdasX p)+ return (XCast a cc x', r)+++-------------------------------------------------------------------------------+-- | Eta-expand partially applied data constructors.+-- The code generator only handles fully applied data constructors,+-- so we eta-expand partially applied ones so that they get turned+-- into thunks.+etaXConApp + :: (Show a, Pretty a, Pretty n, Ord n, Show n, CompoundName n)+ => Profile n -- ^ Language Profile.+ -> Context a n -- ^ Current context of transform.+ -> a -- ^ Annotation from constructor applicatin onde.+ -> Exp a n -- ^ Expression holding constructor.+ -> n -- ^ Name of constructor.+ -> [Exp a n] -- ^ Arguments to constructor.+ -> S (Maybe ( Exp a n+ , Result a n))++etaXConApp !p !c !a !x1 !nCon !xsArg+ -- Lookup the type of the constructor.+ | ctx <- contextCtx c+ , case ctx of+ CtxAppLeft{} -> False+ _ -> True++ , envX <- topOfCtx ctx+ , defs <- EnvX.envxDataDefs envX+ , Just dataCtor <- Map.lookup nCon (DataDef.dataDefsCtors defs)++ -- We're expecting an argument for each of the type and term parameters.+ , arityT <- length $ DataDef.dataCtorTypeParams dataCtor+ , arityX <- length $ DataDef.dataCtorFieldTypes dataCtor+ , arity <- arityT + arityX++ -- Check if the constructor is partially applied.+ , arityX >= 1+ , args <- length xsArg+ , args /= arity+ = do+ -- Make binders for the new type parameters.+ (bsT, usT)+ <- fmap unzip+ $ mapM (\(i, t) -> newVar (show i) t)+ $ [ (i, typeOfBind b)+ | i <- [0..arityT - 1]+ | b <- DataDef.dataCtorTypeParams dataCtor ]++ let sub = zip (DataDef.dataCtorTypeParams dataCtor) + (map TVar usT)++ -- Make binders for the new term parameters.+ (bsX, usX)+ <- fmap unzip+ $ mapM (\(i, t) -> newVar (show i) (substituteTs sub t))+ $ [ (i, t)+ | i <- [0 .. arityX - 1]+ | t <- DataDef.dataCtorFieldTypes dataCtor ]++ -- Transform all the arguments.+ let downArg xArg = enterAppRight c a x1 xArg (lambdasX p)+ (xsArg', rs) <- fmap unzip $ mapM downArg xsArg++ -- Build application of our new abstraction.+ return $ Just+ $ ( xApps a+ ( xLAMs a bsT $ xLams a bsX + $ xApps a (XCon a (DaConBound nCon))+ $ [ XType a (TVar u) | u <- usT]+ ++ [ XVar a u | u <- usX])+ xsArg'++ , mconcat (Result True [] : rs))++ | otherwise+ = return Nothing+
+ DDC/Core/Transform/Lambdas/Base.hs view
@@ -0,0 +1,103 @@++module DDC.Core.Transform.Lambdas.Base+ ( S, evalState+ , newVar+ , newVarExtend+ , Result (..)+ , isLiftyContext)+where+import DDC.Core.Exp.Annot.Ctx+import DDC.Core.Exp.Annot+import DDC.Data.Name+import qualified Control.Monad.State.Strict as S+++---------------------------------------------------------------------------------------------------+-- | State holding a variable name prefix and counter to +-- create fresh variable names.+type S = S.State (String, Int)+++-- | Evaluate a desguaring computation,+-- using the given prefix for freshly introduced variables.+evalState :: String -> S a -> a+evalState n c+ = S.evalState c (n, 0) +++-- | Allocate a new named variable, yielding its associated bind and bound.+newVar + :: CompoundName n+ => String -- ^ Informational name to add.+ -> Type n -- ^ Type of the new binder.+ -> S (Bind n, Bound n)++newVar prefix t+ = do (n, i) <- S.get+ let name' = newVarName (n ++ "$" ++ prefix ++ "$" ++ show i)+ S.put (n, i + 1)+ return (BName name' t, UName name')+++-- | Allocate a new named variable, yielding its associated bind and bound.+newVarExtend+ :: CompoundName n+ => n -- ^ Base name.+ -> String -- ^ Informational name to ad.+ -> Type n -- ^ Type of the new binder.+ -> S (Bind n, Bound n)++newVarExtend name prefix t+ = do (n, i) <- S.get+ let name' = extendName name (n ++ "$" ++ prefix ++ "$" ++ show i)+ S.put (n, i + 1)+ return (BName name' t, UName name')++++---------------------------------------------------------------------------------------------------+-- | Result of lambda lifter recursion.+data Result a n+ = Result+ { -- | Whether we've made any progress in this pass.+ _resultProgress :: Bool ++ -- | Bindings that we've already lifted out, + -- and should be added at top-level.+ , _resultBindings :: [(Bind n, Exp a n)]+ }+++instance Monoid (Result a n) where+ mempty+ = Result False []+ + mappend (Result p1 lts1) (Result p2 lts2)+ = Result (p1 || p2) (lts1 ++ lts2)+++---------------------------------------------------------------------------------------------------+-- | Check if this is a context that we should lift lambda abstractions out of.+isLiftyContext :: Ctx a n -> Bool+isLiftyContext ctx+ = case ctx of+ -- Don't lift out of the top-level context.+ -- There's nowhere else to lift to.+ CtxTop{} -> False+ CtxLetLLet{} -> not $ isTopLetCtx ctx+ CtxLetLRec{} -> not $ isTopLetCtx ctx++ -- Don't lift if we're inside more lambdas.+ -- We want to lift the whole binding group together.+ CtxLAM{} -> False+ CtxLam{} -> False+ + -- We can't do code generation for abstractions in these contexts,+ -- so they need to be lifted.+ CtxAppLeft{} -> True+ CtxAppRight{} -> True+ CtxLetBody{} -> True+ CtxCaseScrut{} -> True+ CtxCaseAlt{} -> True+ CtxCastBody{} -> True+
+ DDC/Core/Transform/Lambdas/Lift.hs view
@@ -0,0 +1,184 @@++module DDC.Core.Transform.Lambdas.Lift+ (liftLambda)+where+import DDC.Core.Transform.Lambdas.Base+import DDC.Core.Fragment+import DDC.Core.Exp.Annot.Context+import DDC.Core.Exp.Annot.Ctx+import DDC.Core.Exp.Annot+import DDC.Core.Collect+import DDC.Data.Pretty+import DDC.Data.Name+import Data.Function+import Data.List+import Data.Set (Set)+import qualified DDC.Core.Check as Check+import qualified DDC.Type.Env as Env+import qualified DDC.Core.Env.EnvX as EnvX+import qualified Data.Set as Set+import qualified Data.Map as Map+++---------------------------------------------------------------------------------------------------+-- | Construct the call site, and new lifted binding for a lambda lifted+-- abstraction.+liftLambda + :: (Show a, Show n, Pretty n, Ord n, CompoundName n, Pretty a)+ => Profile n -- ^ Language profile.+ -> Context a n -- ^ Context of the original abstraction.+ -> Set (Bool, Bound n) -- ^ Free variables in the body of the abstraction.+ -> a+ -> [(Bool, Bind n)] -- ^ Parameters of the current lambda abstraction.+ -> Exp a n -- ^ Body of the current lambda abstraction.+ -> S ( Exp a n+ , Bind n, Exp a n)++liftLambda p c fusFree a bfsParam xBody+ = do + -- Name of the enclosing top-level binding.+ let Just nTop = takeTopNameOfCtx (contextCtx c)++ -- Names of other supers bound at top-level.+ let nsSuper = takeTopLetEnvNamesOfCtx (contextCtx c)++ -- The complete abstraction that we're lifting out.+ let xLambda = makeXLamFlags a bfsParam xBody++ -- Get the list of new parameters we need to add to our super.+ -- These bind all the variables that were free in the abstraction+ -- group that we're lifting out.+ let fusFree_filtered+ = filter (needParamForFreeBound nsSuper bfsParam)+ $ Set.toList fusFree+++ -- Lookup the types of those variables from the context,+ -- so we can add the correct types for the parameters of the new super.+ let joinType (f@True, u)+ | Just t <- EnvX.lookupT u (contextEnv c)+ = ((f, u), t)++ joinType (f@False, u)+ | Just t <- EnvX.lookupX u (contextEnv c)+ = ((f, u), t)++ joinType (f, u)+ = error $ unlines+ [ "ddc-core-simpl.liftLambda: cannot find type of free variable."+ , show (f, u)+ , show (Map.keys $ EnvX.envxMap (contextEnv c)) ]++ let futsFree_types+ = map joinType fusFree_filtered+++ -- Add in type variables that are free in the types of free+ -- value variables. We need to bind these as well in the new super.+ let expandFree ((f@True, u), _)+ = [(f, u)]++ expandFree ((f@False, u), t)+ = [(f, u)]+ ++ [(True, ut) | ut <- Set.toList+ $ freeVarsT Env.empty t]++ let fusFree_body + = [(True, ut) | ut <- Set.toList + $ freeVarsT Env.empty $ typeOfExp p c xLambda]++ let futsFree_expandFree+ = map joinType+ $ Set.toList $ Set.fromList+ $ (concatMap expandFree $ futsFree_types)+ ++ fusFree_body+++ -- Sort free vars so the type variables come out the front.+ let futsFree+ = sortBy (compare `on` (not . fst . fst))+ $ futsFree_expandFree+++ -- Make the new super.+ -- For the lifted abstraction, wrap it in new lambdas to bind all of+ -- its free variables. + -- ISSUE #330: Lambda lifter doesn't work with anonymous binders.+ let makeBind ((True, (UName n)), t) = (True, BName n t)+ makeBind ((False, (UName n)), t) = (False, BName n t)+ makeBind fut+ = error $ "ddc-core-simpl.liftLamba: unhandled binder " ++ show fut+ + let bsParam = map makeBind futsFree+ let xLifted = makeXLamFlags a bsParam xLambda++ -- Make a binder for the new super.+ (bLifted, uLifted) + <- newVarExtend nTop "L" + $ typeOfExp p c xLifted+ + -- At the point where the original abstraction group was, + -- call our new lifted super instead.+ let makeArg (True, u) = XType a (TVar u)+ makeArg (False, u) = XVar a u++ let xCall = xApps a (XVar a uLifted)+ $ map makeArg $ map fst futsFree+ + -- All done.+ return ( xCall+ , bLifted, xLifted)+++---------------------------------------------------------------------------------------------------+-- | Decide whether we need to bind the given variable as a new parameter+-- on the lifted super.+needParamForFreeBound + :: (Ord n)+ => Set n -- ^ Names of supers at top level.+ -> [(Bool, Bind n)] -- ^ Parameters of the current lambda abstraction.+ -> (Bool, Bound n) -- ^ Bound variable, and whether it is a type (True) or not.+ -> Bool++needParamForFreeBound nsSuper bfsParam (bType, u)+ -- We don't need new parameters for supers that are already+ -- at top level, as we can reference those directly.+ | not bType+ , UName n <- u+ = not $ Set.member n nsSuper++ -- We don't need new parameters for primitives, + -- as we can reference those directly.+ | UPrim{} <- u+ = False+ + -- We don't need new paramters for variables that+ -- are already bound by the abstractions that we're lifting.+ | any (boundMatchesBind u . snd) bfsParam+ = False+ + -- We need a new parameter for this free variable.+ | otherwise+ = True+++-- Function to get the type of an expression in the given context.+typeOfExp + :: (Pretty a, Show a, Show n, Pretty n, Eq n, Ord n)+ => Profile n -- ^ Language profile.+ -> Context a n -- ^ Current context.+ -> Exp a n -- ^ Expression we want the type for.+ -> Type n++typeOfExp p c x+ = case Check.typeOfExp+ (Check.configOfProfile p)+ (contextEnv c) x+ of Left err+ -> error $ renderIndent $ vcat+ [ text "ddc-core-simpl.liftLambda: type error in lifted expression"+ , ppr err]++ Right t + -> t+
+ DDC/Core/Transform/Namify.hs view
@@ -0,0 +1,285 @@++-- | Rewriting of anonymous binders to named binders.+module DDC.Core.Transform.Namify+ ( Namify (..)+ , Namifier (..)+ , makeNamifier+ , namifyUnique)+where+import DDC.Core.Module+import DDC.Core.Exp+import DDC.Core.Collect+import DDC.Type.Exp.Simple+import Control.Monad+import DDC.Type.Env (Env, KindEnv, TypeEnv)+import qualified DDC.Type.Sum as Sum+import qualified DDC.Type.Env as Env+import Control.Monad.State.Strict+++-- | Holds a function to rename binders, +-- and the state of the renamer as we decend into the tree.+data Namifier s n+ = Namifier+ { -- | Create a new name for this bind that is not in the given+ -- environment.+ namifierNew :: Env n -> Bind n -> State s n++ -- | Holds the current environment during namification.+ , namifierEnv :: Env n++ -- | Stack of debruijn binders that have been rewritten during+ -- namification.+ , namifierStack :: [Bind n] }+++-- | Construct a new namifier.+makeNamifier + :: (Env n -> Bind n -> State s n) + -- ^ Function to rename binders.+ -- The name chosen cannot be a member of the given+ --- environment.+ -> Env n -- ^ Starting environment of names we cannot use.+ -> Namifier s n++makeNamifier new env+ = Namifier new env []+++-- | Namify a thing, +-- not reusing names already in the program.+namifyUnique+ :: (Ord n, Namify c, BindStruct (c n) n)+ => (KindEnv n -> Namifier s n) -- ^ Make a namifier for level-1 names.+ -> (TypeEnv n -> Namifier s n) -- ^ Make a namifier for level-0 names.+ -> c n+ -> State s (c n)++namifyUnique mkNamK mkNamT xx+ = let (tbinds, xbinds) = collectBinds xx+ namK = mkNamK (Env.fromList tbinds)+ namT = mkNamT (Env.fromList xbinds)+ in namify namK namT xx+++-- Namify ---------------------------------------------------------------------+class Namify (c :: * -> *) where+ -- | Rewrite anonymous binders to named binders in a thing.+ namify :: Ord n+ => Namifier s n -- ^ Namifier for type names (level-1)+ -> Namifier s n -- ^ Namifier for exp names (level-0)+ -> c n -- ^ Rewrite binders in this thing.+ -> State s (c n)+++instance Namify Type where+ namify tnam xnam tt+ = let down = namify tnam xnam+ in case tt of+ TVar u + -> liftM TVar (rewriteT tnam u) ++ TCon{} + -> return tt++ TAbs b t+ -> do (tnam', b') <- pushT tnam b+ t' <- namify tnam' xnam t+ return $ TAbs b' t'++ TApp t1 t2+ -> liftM2 TApp (down t1) (down t2)++ TForall b t+ -> do (tnam', b') <- pushT tnam b+ t' <- namify tnam' xnam t+ return $ TForall b' t'++ TSum ts + -> do ts' <- mapM down $ Sum.toList ts+ return $ TSum $ Sum.fromList (Sum.kindOfSum ts) ts'+++instance Namify (Module a) where+ namify tnam xnam mm + = do body' <- namify tnam xnam $ moduleBody mm+ return $ mm { moduleBody = body' }+++instance Namify (Witness a) where+ namify tnam xnam ww+ = let down = namify tnam xnam+ in case ww of+ WVar a u -> liftM (WVar a) (rewriteX tnam xnam u)+ WCon{} -> return ww+ WApp a w1 w2 -> liftM2 (WApp a) (down w1) (down w2)+ WType a t -> liftM (WType a) (down t)+++instance Namify (Exp a) where+ namify tnam xnam xx+ = {-# SCC namify #-}+ let down = namify tnam xnam+ in case xx of+ XVar a u -> liftM2 XVar (return a) (rewriteX tnam xnam u)+ XCon{} -> return xx++ XLAM a b x+ -> do (tnam', b') <- pushT tnam b+ x' <- namify tnam' xnam x+ return $ XLAM a b' x'++ XLam a b x+ -> do (xnam', b') <- pushX tnam xnam b+ x' <- namify tnam xnam' x+ return $ XLam a b' x'++ XApp a x1 x2 + -> liftM3 XApp (return a) (down x1) (down x2)++ XLet a (LLet b x1) x2+ -> do x1' <- namify tnam xnam x1+ (xnam', b') <- pushX tnam xnam b+ x2' <- namify tnam xnam' x2+ return $ XLet a (LLet b' x1') x2'++ XLet a (LRec bxs) x2+ -> do let (bs, xs) = unzip bxs+ (xnam', bs') <- pushXs tnam xnam bs+ xs' <- mapM (namify tnam xnam') xs+ x2' <- namify tnam xnam' x2+ return $ XLet a (LRec (zip bs' xs')) x2'++ XLet a (LPrivate b mt bs) x2+ -> do (tnam', b') <- pushTs tnam b+ (xnam', bs') <- pushXs tnam' xnam bs+ x2' <- namify tnam' xnam' x2+ return $ XLet a (LPrivate b' mt bs') x2'++ XCase a x1 alts -> liftM2 (XCase a) (down x1) (mapM down alts)+ XCast a c x -> liftM2 (XCast a) (down c) (down x)+ XType a t -> liftM (XType a) (down t)+ XWitness a w -> liftM (XWitness a) (down w)+++instance Namify (Alt a) where+ namify tnam xnam (AAlt PDefault x)+ = liftM (AAlt PDefault) (namify tnam xnam x)++ namify tnam xnam (AAlt (PData u bs) x)+ = do (xnam', bs') <- pushXs tnam xnam bs+ x' <- namify tnam xnam' x+ return $ AAlt (PData u bs') x'+++instance Namify (Cast a) where+ namify tnam xnam cc+ = let down = namify tnam xnam+ in case cc of+ CastWeakenEffect eff -> liftM CastWeakenEffect (down eff)+ CastPurify w -> liftM CastPurify (down w)+ CastBox -> return CastBox+ CastRun -> return CastRun+++-- | Rewrite level-1 anonymous binders.+rewriteT :: Namifier s n+ -> Bound n+ -> State s (Bound n)++rewriteT tnam u+ = case u of+ UIx i+ -> case lookup i (zip [0..] (namifierStack tnam)) of+ Just (BName n _) -> return $ UName n+ _ -> return u++ _ -> return u+++-- | Rewrite level-0 anonymous binders.+rewriteX :: Namifier s n+ -> Namifier s n+ -> Bound n+ -> State s (Bound n)++rewriteX _tnam xnam u+ = case u of+ UIx i+ -> case lookup i (zip [0..] (namifierStack xnam)) of+ Just (BName n _) + -> do return $ UName n+ _ -> return u++ _ -> return u+++-- Push -----------------------------------------------------------------------+-- Chosing new names for anonymous binders and pushing them on the stack.++-- | Push a level-0 binder on the stack.+-- When we do this we also rewrite any indices in its type annotation.+pushX :: Ord n+ => Namifier s n+ -> Namifier s n+ -> Bind n+ -> State s (Namifier s n, Bind n) ++pushX tnam xnam b+ = do t' <- namify tnam xnam (typeOfBind b)+ let b' = replaceTypeOfBind t' b+ push xnam b'+++-- | Push some level-0 binders on the stack.+-- When we do this we also rewrite their type annotations.+pushXs :: Ord n+ => Namifier s n+ -> Namifier s n+ -> [Bind n]+ -> State s (Namifier s n, [Bind n])++pushXs _tnam xnam [] + = return (xnam, [])++pushXs tnam xnam (b:bs)+ = do (xnam1, b') <- pushX tnam xnam b+ (xnam2, bs') <- pushXs tnam xnam1 bs+ return (xnam2, b' : bs')+++-- | Push a level-1 binder on the stack.+pushT :: Ord n+ => Namifier s n+ -> Bind n+ -> State s (Namifier s n, Bind n)+pushT = push+++pushTs :: Ord n+ => Namifier s n+ -> [Bind n]+ -> State s (Namifier s n, [Bind n])+pushTs tnam [] = return (tnam, [])+pushTs tnam (b:bs)+ = do (tnam1, b') <- pushT tnam b+ (tnam2, bs') <- pushTs tnam1 bs+ return (tnam2, b' : bs')+ ++-- | Rewrite an anonymous binder and push it on the stack.+push :: Ord n + => Namifier s n + -> Bind n + -> State s (Namifier s n, Bind n)++push nam b+ = case b of+ BAnon t+ -> do n <- namifierNew nam (namifierEnv nam) b+ let b' = BName n t+ return ( nam { namifierStack = b' : namifierStack nam + , namifierEnv = Env.extend b (namifierEnv nam) }+ , b' )+ _ -> return ( nam { namifierEnv = Env.extend b (namifierEnv nam) }+ , b)
+ DDC/Core/Transform/Prune.hs view
@@ -0,0 +1,213 @@++-- | Erase contained let-bindings that have no uses.+--+-- Contained bindings are ones that do not perform effects that are+-- visible to anything in the calling context. This includes allocation+-- and read effects, but not writes or any globally visible effects.+--+module DDC.Core.Transform.Prune+ ( PruneInfo (..)+ , pruneModule+ , pruneX)+where+import DDC.Core.Analysis.Usage+import DDC.Core.Simplifier.Base+import DDC.Core.Transform.Reannotate+import DDC.Core.Transform.TransformUpX+import DDC.Core.Fragment+import DDC.Core.Check+import DDC.Core.Module+import DDC.Core.Exp+import DDC.Data.Pretty+import Data.Typeable+import Control.Monad.Writer (Writer, runWriter, tell)+import DDC.Core.Env.EnvX (EnvX)+import qualified Data.Map as Map+import qualified DDC.Core.Transform.SubstituteXX as S+import qualified DDC.Type.Exp.Simple as T+import qualified DDC.Type.Sum as TS+import qualified DDC.Core.Env.EnvT as EnvT+import Prelude hiding ((<$>))+++-------------------------------------------------------------------------------+-- | A summary of what the prune transform did.+data PruneInfo+ = PruneInfo+ { -- | How many let-bindings we erased.+ infoBindingsErased :: Int }+ deriving Typeable+++instance Pretty PruneInfo where+ ppr (PruneInfo remo)+ = text "Prune:"+ <$> indent 4 (vcat+ [ text "Removed: " <> int remo])+++instance Monoid PruneInfo where+ mempty = PruneInfo 0++ mappend (PruneInfo r1) (PruneInfo r2)+ = PruneInfo (r1 + r2)+++-------------------------------------------------------------------------------+-- | Erase pure let-bindings in a module that have no uses.+pruneModule+ :: (Show a, Show n, Ord n, Pretty n)+ => Profile n -- ^ Profile of the language we're in+ -> Module a n+ -> Module a n++pruneModule profile mm+ -- If the language fragment has untracked effects then we can't do+ -- the prune transform because we risk dropping statements with global+ -- effects.+ | not $ featuresTrackedEffects+ $ profileFeatures profile+ = mm++ | otherwise+ = let env = moduleEnvX + (profilePrimKinds profile)+ (profilePrimTypes profile)+ (profilePrimDataDefs profile)+ mm+ in mm { moduleBody + = result $ pruneX profile env+ $ moduleBody mm }+++-- | Erase pure let-bindings in an expression that have no uses.+pruneX+ :: (Show a, Show n, Ord n, Pretty n)+ => Profile n -- ^ Profile of the language we're in+ -> EnvX n -- ^ Type checker environment.+ -> Exp a n+ -> TransformResult (Exp a n)++pruneX profile env xx+ = {-# SCC pruneX #-}+ let + (xx', info)+ = transformTypeUsage profile env+ (transformUpMX pruneTrans env)+ xx++ progress (PruneInfo r) + = r > 0++ in TransformResult+ { result = xx'+ , resultAgain = progress info+ , resultProgress = progress info+ , resultInfo = TransformInfo info }+++-- The prune transform proper needs to have every expression annotated+-- with its type an effect, as well the variable usage map.+--+-- We generate these annotations here then pass the result off to+-- deadCodeTrans to actually erase dead bindings.+--+transformTypeUsage profile env trans xx+ = let config = configOfProfile profile+ rr = checkExp config env Recon DemandNone xx+ in case fst rr of+ Right (xx1, _, _) + -> let xx2 = usageX xx1+ (x', info) = runWriter (trans xx2)+ x'' = reannotate (\(_, AnTEC { annotTail = a }) -> a) x'+ in (x'', info)++ Left _+ -> error $ renderIndent+ $ vcat [ text "ddc-core-simpl.Prune: core type error" ]+++-------------------------------------------------------------------------------+-- | Annotations used by the dead-code trasnform.+type Annot a n + = (UsedMap n, AnTEC a n)+++-- | Apply the dead-code transform to an annotated expression.+pruneTrans+ :: Ord n+ => EnvX n -- ^ Type checker environment.+ -> Exp (Annot a n) n -- ^ Expression to transform.+ -> Writer PruneInfo + (Exp (Annot a n) n)++pruneTrans _ xx+ = case xx of+ XLet a@(usedMap, antec) (LLet b x1) x2+ | isUnusedBind b usedMap+ , isContainedEffect $ annotEffect antec+ -> do + -- We still need to substitute value into casts+ let x2' = S.substituteXX b x1 x2++ -- Record that we've erased a binding.+ tell mempty {infoBindingsErased = 1}++ -- + return $ XCast a (weakEff antec)+ $ x2'++ _ -> return xx++ where+ weakEff antec+ = CastWeakenEffect+ $ T.crushEffect EnvT.empty+ $ annotEffect antec+++-- | Check whether this binder has no uses, +-- not including weakclo casts, beause we'll substitute the bound+-- expression directly into those.+isUnusedBind :: Ord n => Bind n -> UsedMap n -> Bool+isUnusedBind bb (UsedMap um)+ = case bb of+ BName n _+ -> case Map.lookup n um of+ Just useds -> filterUsedInCasts useds == []+ Nothing -> True++ BNone _ -> True+ _ -> False+++filterUsedInCasts :: [Used] -> [Used]+filterUsedInCasts = filter notCast+ where notCast UsedInCast = False+ notCast _ = True+++-- | A contained effect is one that is not visible to anything else+-- in the context. This is allocation and read effects, which are+-- not visible from outside the computation performing the effect. +isContainedEffect :: Ord n => Effect n -> Bool+isContainedEffect eff + = all contained+ $ map T.takeTApps + $ sumList + $ T.crushEffect EnvT.empty eff+ where+ contained (c : _args)+ = case c of+ TCon (TyConSpec TcConAlloc) -> True+ TCon (TyConSpec TcConDeepAlloc) -> True+ TCon (TyConSpec TcConRead) -> True+ TCon (TyConSpec TcConHeadRead) -> True+ TCon (TyConSpec TcConDeepRead) -> True+ _ -> False++ contained [] = False++ sumList (TSum ts) = TS.toList ts+ sumList tt = [tt]+
+ DDC/Core/Transform/Rewrite.hs view
@@ -0,0 +1,546 @@++-- | Apply rewrite rules.+module DDC.Core.Transform.Rewrite+ ( RewriteRule(..)+ , rewriteModule+ , rewriteX)+where+import DDC.Data.Pretty+import DDC.Core.Exp.Annot as X+import DDC.Core.Module+import Data.Map (Map)+import DDC.Core.Simplifier.Base (TransformResult(..), TransformInfo(..))+import qualified DDC.Core.Transform.AnonymizeX as A+import qualified DDC.Core.Transform.Rewrite.Disjoint as RD+import qualified DDC.Core.Transform.Rewrite.Env as RE+import qualified DDC.Core.Transform.Rewrite.Match as RM+import DDC.Core.Transform.Rewrite.Rule+import qualified DDC.Core.Transform.SubstituteXX as S+import qualified DDC.Type.Transform.SubstituteT as S+import qualified DDC.Core.Transform.BoundX as L+import qualified DDC.Type.Exp.Simple as T+import qualified Data.Map as Map+import qualified Data.Set as Set+import Data.Maybe+import Control.Monad+import Control.Monad.Writer (tell, runWriter)+import Data.List+import Data.Typeable+import Prelude hiding ((<$>))+++-- Log ------------------------------------------------------------------------+-- | Tracks which rewrite rules fired.+data RewriteInfo + = RewriteInfo [RewriteLog]+ deriving Typeable++data RewriteLog+ = LogRewrite String+ | LogUnfold String+ deriving Typeable+++instance Pretty RewriteInfo where+ ppr (RewriteInfo rules) + = text "Rules fired:"+ <$> indent 4 (vcat $ map ppr rules)+++instance Pretty RewriteLog where+ ppr (LogRewrite name) = text "Rewrite: " <> text name+ ppr (LogUnfold name) = text "Unfold: " <> text name++isProgress = not . null+++-- Rewrite --------------------------------------------------------------------+-- | Apply rewrite rules to a module.+rewriteModule+ :: (Show a, Show n, Ord n, Pretty n)+ => [NamedRewriteRule a n] -- ^ Rewrite rule database.+ -> Module a n -- ^ Rewrite in this module.+ -> Module a n++rewriteModule rules mm+ = mm { moduleBody = result $ rewriteX' True rules $ moduleBody mm }+++-- | Perform rewrites top-down, repeatedly.+rewriteX + :: (Show a, Show n, Ord n, Pretty n) + => [NamedRewriteRule a n] -- ^ Rewrite rules database.+ -> Exp a n -- ^ Rewrite in this expression.+ -> TransformResult (Exp a n)+rewriteX = rewriteX' False+++-- | Repeatedly perform rewrites top-down.+-- Usually any names bound in @letrec@s disable rules, because the+-- new binding makes the old rule meaningless.+-- For modules we do not want to do this, since the rules for that module+-- are probably about the functions exported from the module.+-- In this case, we ignore the top-level bindings when checking for rule shadowing.+rewriteX'+ :: (Show a, Show n, Ord n, Pretty n) + => Bool -- ^ Ignore top-level bindings when checking for shadowing?+ -> [NamedRewriteRule a n] -- ^ Rewrite rules database.+ -> Exp a n -- ^ Rewrite in this expression.+ -> TransformResult (Exp a n)++rewriteX' ignore_toplevel rules x0+ = {-# SCC rewriteX #-}+ let (x,info) = runWriter $ go RE.empty x0 [] ignore_toplevel+ in TransformResult+ { result = x+ , resultAgain = isProgress info+ , resultProgress = isProgress info+ , resultInfo = TransformInfo (RewriteInfo info) }+ where+ -- ISSUE #280: Rewrites should be done with the most specific rule.+ -- The rewrite engine should apply the most specific rule, instead+ -- of the first one that it finds that matches. If not, then we+ -- should give some warning about overlapping rules.+ -- + -- Look for rules in the list that match the given expression,+ -- and apply the first one that matches.+ rewrites env f args+ = rewrites' rules env f args++ rewrites' [] _ f args+ = return $ X.makeXAppsWithAnnots f args++ rewrites' ((n, rule) : rs) env f args+ = case rewriteWithX rule env f args of+ Nothing -> rewrites' rs env f args+ Just x -> tell [LogRewrite n] >> go env x [] False+++ down env x + = go env x [] False+++ -- Decend into the expression, looking for applications that we + -- might be able to apply rewrites to.+ go env (XApp a f arg) args _toplevel+ = do arg' <- down env arg+ go env f ((arg',a):args) False++ go env x@XVar{} args _toplevel+ = rewrites env x args++ go env x@XCon{} args _toplevel+ = rewrites env x args++ go env (XLAM a b e) args _toplevel+ = do e' <- down (RE.lift b env) e + rewrites env (XLAM a b e') args++ go env (XLam a b e) args _toplevel+ = do e' <- down (RE.extend b env) e+ rewrites env (XLam a b e') args++ go env (XLet a l@(LRec _) e) args toplevel+ = do -- Don't add the @letrec@'s bindings to the rule shadow list if we're at the top-level+ let env' = if toplevel+ then env+ else RE.extendLets l env+ l' <- goLets l env'+ e' <- down env' e + rewrites env' (XLet a l' e') args++ go env (XLet a l e) args _toplevel+ = do l' <- goLets l env+ dh <- goDefHoles rules a l' e env down+ rewrites env dh args ++ go env (XCase a e alts) args _toplevel+ = do e' <- down env e+ alts' <- mapM (goAlts env) alts+ rewrites env (XCase a e' alts') args++ go env (XCast a c e) args _toplevel+ = do e' <- down env e+ rewrites env (XCast a c e') args++ go env x@(XType{}) args _toplevel+ = rewrites env x args++ go env x@(XWitness{}) args _toplevel+ = rewrites env x args+++ goLets (LLet b e) ws + = do e' <- down ws e + return $ LLet b e'++ goLets (LRec bs) ws + = do bs' <- mapM (down ws) $ map snd bs+ return $ LRec $ zip (map fst bs) bs'+++ goLets l _ + = return $ l++ goAlts ws (AAlt p e) + = do e' <- down ws e + return $ AAlt p e'+++-- If definitions match the holes of any rules,+-- clean it up and record it for later.+-- Eg with this rule,+-- RULE unbox {box s} = s+--+-- this expression:+-- let x = box (some expensive op)+-- in ...+--+-- will be transformed to+-- let ^ = some expensive op+-- x = box ^0+-- in ...+--+goDefHoles rules a l@(LLet let_bind def) e env down+ | (((sub, []), name, RewriteRule { ruleBinds = bs, ruleLeft = hole }):_)+ <- checkHoles rules def env++ = let -- only get value-level bindings+ bs' = filter (isBMValue . fst) bs+ bas' = lookupFromSubst a bs' sub++ -- check if it looks like something has already been unfolded+ isUIx x = case x of + XVar _ (UIx _) -> True+ XVar _ (UPrim _ _) -> True+ _ -> False++ already_done+ = all isUIx $ map snd bas'++ -- find kind-values and sub those in as well+ bsK' = filter ((== BMSpec) . fst) bs+ basK = lookupFromSubst a bsK' sub++ basK' = concatMap (\(b,x) -> case X.takeXType x of+ Just t -> [(b,t)]+ Nothing-> []) basK++ -- surround whole expression with anon lets from sub+ values = map (\(b,v) -> (BAnon (S.substituteTs basK' $ T.typeOfBind b), v))+ (reverse bas')++ -- replace 'def' with LHS-HOLE[sub => ^n]+ anons = zipWith (\(b,_) i -> (b, XVar a (UIx i))) bas' [0..]+ lets = map (\(b,v) -> LLet b v) values++ def' = S.substituteXArgs basK+ $ S.substituteXArgs anons hole++ let_bind' = S.substituteTs basK' let_bind+ lets' = lets ++ [LLet let_bind' def']++ -- lift e by (length bas)+ depth = case let_bind of+ BAnon _ -> 1 + _ -> 0++ e' = L.liftAtDepthX (length bas') depth e++ -- SAVE in wit env+ env' = foldl (flip RE.extendLets) env lets'++ in if already_done+ then do+ e'' <- down (RE.extendLets l env) e+ return $ XLet a l e''++ else do+ tell [LogUnfold name]+ e'' <- down env' e'+ return $ X.xLets a lets' e''++ | otherwise+ = do e' <- down (RE.extendLets l env) e+ return $ XLet a l e'++goDefHoles _rules a l e env down+ = do e' <- down (RE.extendLets l env) e+ return $ XLet a l e'+++-- Match a let-definition against the holes in all the rules+checkHoles + :: (Show n, Show a, Ord n, Pretty n)+ => [NamedRewriteRule a n]+ -> Exp a n+ -> RE.RewriteEnv a n+ -> [ ( (RM.SubstInfo a n, [(Exp a n, a)])+ , String+ , RewriteRule a n) ]++checkHoles rules def ws+ = let rules' = catMaybes $ map holeRule rules+ (f,args) = X.takeXAppsWithAnnots def++ in catMaybes+ $ map (\(name,r) -> fmap (\s -> (s,name,r)) + $ matchWithRule r ws f args RM.emptySubstInfo)+ rules'+++holeRule (name, rule@RewriteRule { ruleLeftHole = Just hole })+ = Just ( name+ , rule { ruleLeft = hole+ , ruleLeftHole = Nothing })++holeRule _ = Nothing+++-------------------------------------------------------------------------------+-- | Perform rewrite rule on expression if a valid substitution exists,+-- and constraints are satisfied.+rewriteWithX+ :: (Show n, Show a, Ord n, Pretty n)+ => RewriteRule a n+ -> RE.RewriteEnv a n+ -> Exp a n+ -> [(Exp a n, a)]+ -> Maybe (Exp a n)++rewriteWithX rule env f args + = do + let RewriteRule+ { ruleBinds = binds+ , ruleConstraints = constrs+ , ruleRight = rhs+ , ruleWeakEff = eff+ , ruleWeakClo = _clo } + = rule+ + -- Try to find a substitution for the left of the rule.+ (m, rest) <- matchWithRule rule env f args RM.emptySubstInfo++ -- Check constraints, perform substitution and add weakens if necessary.+ let a = X.annotOfExp f++ let bas2 = lookupFromSubst a binds m+ let rhs2 = A.anonymizeX rhs+ let (bas3,lets) = wrapLets a binds bas2+ let rhs3 = L.liftX (length lets) rhs2++ -- Substitute bindings into the effect of the right of the rule.+ let eff' = liftM (substT bas3) eff++ -- Substitute bindings into rule constraints and+ -- check that they are all satisfied by the environment.+ let constrs' = map (substT bas3) constrs+ when (not $ all (satisfiedContraint env) constrs')+ $ Nothing++ -- Build the rewritten expression.+ let x' = X.xLets a lets+ $ weakeff a eff'+ $ S.substituteXArgs bas3 rhs3++ -- Add the remaining arguments from the original expression+ -- that weren't matched by rule+ return $ X.makeXAppsWithAnnots x' rest+++-- | Check whether we can satisfy this constraint using witnesses+-- in the rewrite nevironment.+satisfiedContraint :: Ord n => RE.RewriteEnv a n -> Type n -> Bool+satisfiedContraint env c+ = RE.containsWitness c env+ || RD.checkDisjoint c env+ || RD.checkDistinct c env+++-- | Wrap an expression in an effect weakning.+weakeff :: a -> Maybe (Effect n) + -> Exp a n -> Exp a n++weakeff a meff x+ = maybe x (\e -> XCast a (CastWeakenEffect e) x) meff+++wrapLets+ :: Ord n + => a + -> [(BindMode, Bind n)] -- ^ Variables bound by the rule.+ -> [(Bind n, Exp a n)] -- ^ Substitution for the left of the rule.+ -> ( [(Bind n, Exp a n)]+ , [Lets a n])++wrapLets a binds bas + = let isMkLet (_, (BMValue i, _)) = i /= 1+ isMkLet _ = False++ (as, bs'') = partition isMkLet (bas `zip` binds)+ as' = map fst as+ bs' = map fst bs''++ anons = zipWith (\(b,_) i -> (b, XVar a (UIx i))) as' [0..]+ values = map (\(b,v) -> (BAnon (substT bs' $ T.typeOfBind b), v)) + (reverse as')+ lets = map (\(b,v) -> LLet b v) values++ in (bs' ++ anons, lets)+++-- | Substitute type bindings into a type.+substT :: Ord n => [(Bind n, Exp a n)] -> Type n -> Type n+substT bas x + = let sub = [(b, t) | (b, XType _ t) <- bas ] + in S.substituteTs sub x+++-------------------------------------------------------------------------------+-- | Attempt to find a rewrite substitution to match expression against rule.+-- Returns substitution and the left-over arguments that weren't matched+-- against.+--+-- matchWithRule+-- (RULE mapMapId [a b : *] (f : a -> b) (xs : List a).+-- map [:a b:] f (map [:a a:] id xs)+-- = map [:a b:] f xs)+-- map+-- [ [Int], [Int], (\x -> f), (map [:Int Int:] id [1,2,3]) ]+--+-- env+--+-- emptySubstInfo+-- ==>+-- Just ({a |-> Int, b |-> Int, f |-> (\x -> f), xs |-> [1,2,3] }, [])+--+-- However if we had passed a substitution such as {a |-> Float} instead of+-- emptySubstInfo, it would not have matched.+--+-- The environment is used for 'hole' rules, that can look up bound definitions+-- and match if inlining would let them, even when inlining won't occur.+--+matchWithRule+ :: (Show n, Show a, Ord n, Pretty n)+ => RewriteRule a n -- ^ Rule to unify with.+ -> RE.RewriteEnv a n -- ^ Environment to rewrite in, contains witnesses+ -- for the constraints on rules.+ -> Exp a n -- ^ Function-part of the expression to rewrite.+ -> [(Exp a n, a)] -- ^ Arguments of expression to rewrite, with the + -- annotations we took from the XApp nodes.+ -> RM.SubstInfo a n -- ^ Existing substitution to match with++ -> Maybe ( RM.SubstInfo a n+ , [(Exp a n, a)])+ -- ^ Substitution map and remaining (unmatched) args++-- Handle a rule without a hole.+matchWithRule+ RewriteRule+ { ruleBinds = binds+ , ruleLeft = lhs+ , ruleLeftHole = Nothing+ , ruleFreeVars = free }+ env f args sub+ = do + -- Check that none of the free variables have been rebound.+ when (any (flip RE.hasDef env) free)+ $ Nothing++ -- Get names of all the variables bound by the rule.+ -- This should always match because checked rules are guaranteed not to+ -- have `BAnon` or `BNone` binders.+ let Just vs + = liftM Set.fromList+ $ sequence + $ map T.takeNameOfBind+ $ map snd binds++ -- Split the left part of the rule in to the function part and its+ -- arguments.+ l:ls <- return $ X.takeXAppsAsList lhs++ -- Match the function part of the expression against+ -- the function part of the rule.+ sub' <- RM.match sub vs l f++ -- Match each of the expression arguments against + -- the arguments of the rule.+ let go m [] rest+ = do return $ (m, rest)++ go m (l':ls') ((r,_):rs)+ = do m' <- RM.match m vs l' r+ go m' ls' rs++ go _ _ _ + = Nothing++ go sub' ls args+++-- Handle a rule with a hole. +-- An example rule with a holes is:+-- RULE (i : Int). unbox {box i} = i+matchWithRule+ rule@(RewriteRule { ruleLeftHole = Just hole })+ env f args sub++ -- Try to match against entire rule with no inlining.+ -- Eg (unbox (box 5))+ | a <- X.annotOfExp f+ , lhs_full <- XApp a (ruleLeft rule) hole + , rule_full <- rule { ruleLeft = lhs_full, ruleLeftHole = Nothing}+ , Just subst <- matchWithRule rule_full env f args sub+ = Just subst++ -- Try to match against the part without the hole.+ -- eg unifyWithRule (RULE (i : Int). unbox) (unbox x)+ -- which will return a substitution (empty here),+ -- and the leftover argument 'x'.+ | rule_some <- rule { ruleLeftHole = Nothing }+ , Just (sub', (XVar _ b, _) : as)+ <- matchWithRule rule_some env f args sub++ -- See if the 'x' variable is let-bound in an outer scope+ , Just d' <- RE.getDef b env + , (fd, ad) <- X.takeXAppsWithAnnots d' ++ -- Match the hole part with the right of the 'x' binding.+ -- This completes the match, so we merge this new substitution+ -- with the one for the outer part of the rule.+ , rule_hole <- rule { ruleLeft = hole, ruleLeftHole = Nothing }+ , Just (subd, asd) <- matchWithRule rule_hole env fd ad sub'+ = Just (subd, asd ++ as)++ -- rule_some didn't match properly: failure+ | otherwise+ = Nothing+++-------------------------------------------------------------------------------+-- | Lookup a binding from a rewrite rule substitution.+--+-- Eg: RULE [x : %] (x : Int x). ...+-- +lookupFromSubst + :: Ord n+ => a+ -> [(BindMode,Bind n)]+ -> (Map n (Exp a n), Map n (Type n))+ -> [(Bind n, Exp a n)]++lookupFromSubst a1 bs m+ = let bas = catMaybes $ map (lookupX m) bs+ in map (\(b, a) -> (A.anonymizeX b, A.anonymizeX a)) bas+ + where lookupX (xs,_) (BMValue _, b@(BName n _))+ | Just x <- Map.lookup n xs+ = Just (b, x)++ lookupX (_,tys) (BMSpec, b@(BName n _))+ | Just t <- Map.lookup n tys+ = Just (b, XType a1 t)++ lookupX _ _ = Nothing+
+ DDC/Core/Transform/Rewrite/Disjoint.hs view
@@ -0,0 +1,202 @@+-- | Check whether two effects are non-interfering+module DDC.Core.Transform.Rewrite.Disjoint+ ( checkDisjoint+ , checkDistinct )+where+import DDC.Core.Exp+import DDC.Type.Exp.Simple as T+import qualified DDC.Core.Transform.Rewrite.Env as RE+import qualified DDC.Type.Sum as Sum+import qualified DDC.Core.Env.EnvT as EnvT+++-- | Check whether a disjointness property is true in the given+-- rewrite environment.+--+-- Disjointness means that two effects do not interfere.+--+-- Context is important because if two regions are known to be+-- distinct, reading from one and writing to another is valid.+-- If they have different names they may not be distinct.+--+-- All read effects are disjoint with other reads.+--+-- > Disjoint (Read r1) (Read r2)+-- > Disjoint (Read r1) (DeepRead a)+--+-- Allocation effects are disjoint with everything.+--+-- > Disjoint (Alloc r) (_)+--+-- Atomic reads and write effects are disjoint if they are to distinct regions.+--+-- > Distinct r1 r2+-- > -----------------------------+-- > Disjoint (Read r1) (Write r2)+-- +-- @DeepWrite@ effects are only disjoint with allocation effects, because+-- we don't know what regions it will write to.+--+-- An effect sum is disjoint from some other effect if all its components are.+--+-- > Disjoint f1 g /\ Disjoint f2 g+-- > -----------------------------+-- > Disjoint (f1 + f2) g+--+-- Disjointness is commutative.+--+-- > Disjoint f g+-- > ------------+-- > Disjoint g f+-- +-- Example:+-- +-- > checkDisjoint+-- > (Disjoint (Read r1 + Read r2) (Write r3))+-- > [Distinct r1 r3, Distinct r2 r3]+-- > = True+--+checkDisjoint+ :: Ord n+ => Type n -- ^ Type of property we want+ -- eg @Disjoint e1 e2@+ -> RE.RewriteEnv a n -- ^ Environment we're rewriting in.+ -> Bool++checkDisjoint c env+ -- The type must have the form "Disjoint e1 e2"+ | [TCon (TyConWitness TwConDisjoint), fs, gs] <- takeTApps c+ = and [ areDisjoint env g f + | f <- sumList $ T.crushEffect EnvT.empty fs+ , g <- sumList $ T.crushEffect EnvT.empty gs ]++ | otherwise+ = False+ where sumList (TSum ts) = Sum.toList ts+ sumList tt = [tt]+++-- | Check whether two atomic effects are disjoint.+areDisjoint + :: Ord n+ => RE.RewriteEnv a n+ -> Effect n+ -> Effect n+ -> Bool++areDisjoint env t1 t2+ -- Allocations are disjoint with everything.+ | isSomeAllocEffect t1+ || isSomeAllocEffect t2+ = True ++ -- All the read effects are disjoint with each other.+ | isSomeReadEffect t1+ , isSomeReadEffect t2+ = True++ -- Combinations of reads and writes are disjoint+ -- if the regions are distinct.+ | TApp _ tR1 <- t1+ , TApp _ tR2 <- t2+ , (isReadEffect t1 && isWriteEffect t2)+ || (isWriteEffect t1 && isReadEffect t2)+ || (isWriteEffect t1 && isWriteEffect t2)+ = areDistinct env tR1 tR2++ -- All other effects are assumed to be interfering.+ | otherwise = False+++-- Distinct -------------------------------------------------------------------+-- | Check whether a distintness property is true in the given +-- rewrite environment.+--+-- Distinctness means that two regions do not alias.+--+checkDistinct+ :: Ord n+ => Type n -- ^ Type of the property we want,+ -- eg @Distinct r1 r2@+ -> RE.RewriteEnv a n -- ^ Environment we're rewriting in.+ -> Bool++checkDistinct c env+ -- It's of the form "Distinct r q"+ | (TCon (TyConWitness (TwConDistinct _)) : args)+ <- takeTApps c+ = all (uncurry $ areDistinct env) (combinations args)++ | otherwise+ = False++ where combinations [] = []+ combinations (x:xs) = repeat x `zip` xs ++ combinations xs+++-- | Check if two regions are distinct.+areDistinct+ :: Ord n+ => RE.RewriteEnv a n+ -> Type n -> Type n -> Bool++areDistinct env t1 t2+ | Just u1 <- takeBound t1+ , Just u2 <- takeBound t2+ = areDistinctBound env u1 u2++ | otherwise+ = False+ where takeBound (TVar u) = Just u+ takeBound (TCon (TyConBound u _)) = Just u+ takeBound _ = Nothing+++-- | Check whether two regions are distinct.+-- This version takes `Bounds` so we don't need to worry about+-- region constructors like R0# directly.+areDistinctBound + :: Ord n+ => RE.RewriteEnv a n+ -> Bound n -> Bound n -> Bool++areDistinctBound env p q+ -- If they are the same, they can't possibly be different+ | p == q+ = False++ -- If they're both named primitives (eg R0#, R1#)+ -- we can just check name-equality, since can't be bound in lambdas+ -- Or if they're both bound in letregions, we can check by name+ -- (and we know names are different because that's an insta-fail)+ | concrete p && concrete q+ = True++ -- Check witness map for "Distinct p q" and vice versa+ | any check $ RE.getWitnesses env+ = True++ -- Otherwise not.+ | otherwise+ = False++ where -- Check if region is 'concrete' either a region handle (R0#) + -- or bound by a letregion in a higher scope.+ concrete r+ = case r of+ UPrim _ _ -> True+ _ -> RE.containsRegion r env++ check w+ | (TCon (TyConWitness (TwConDistinct _)) : args)+ <- takeTApps w+ = rgn p `elem` args && rgn q `elem` args++ | otherwise+ = False++ rgn b+ = case b of+ UPrim _ t -> TCon (TyConBound b t)+ _ -> TVar b+
+ DDC/Core/Transform/Rewrite/Env.hs view
@@ -0,0 +1,215 @@+module DDC.Core.Transform.Rewrite.Env+ ( RewriteEnv+ , empty+ , extend+ , extendLets+ , containsRegion+ , containsWitness+ , getWitnesses+ , insertDef+ , getDef+ , hasDef+ , lift+ , liftValue)+where+import DDC.Core.Exp+import qualified DDC.Type.Exp.Simple as T+import qualified DDC.Type.Transform.BoundT as L+import qualified DDC.Core.Transform.BoundX as L+import Data.Maybe (fromMaybe, listToMaybe, isJust)+++-- | A summary of the environment that we perform a rewrite in.+--+-- As we decend into the program looking for expressions to rewrite, +-- we keep track of what information as been defined in the environment+-- in a `RewriteEnv`.+--+-- When we go under an anonymous binder then we push a new outermost+-- list instead of lifting every element on the environment eagerly.+-- +data RewriteEnv a n + = RewriteEnv+ { -- | Types of all witnesses in scope.+ -- We use these to satisfy constraints on rewrite rules like Const r.+ witnesses :: [[T.Type n]]++ -- | Names of letregion-bound regions:+ -- this is interesting because they must be distinct.+ , letregions :: [[Bind n]]++ -- | Assoc of known values+ -- If going to inline them, they must only reference de bruijn binds+ -- these are value-level bindings, so be careful lifting.+ , defs :: [[RewriteDef a n]] }+ deriving (Show,Eq)+++type RewriteDef a n + = (Bind n, Maybe (Exp a n))+++-- | An empty environment.+empty :: RewriteEnv a n+empty = RewriteEnv [] [] []+++-- | Extend an environment with some lambda-bound binder (XLam)+-- Might be a witness. Don't count if it's a region.+extend :: Ord n => Bind n -> RewriteEnv a n -> RewriteEnv a n+extend b env+ | T.isWitnessType (T.typeOfBind b)+ = let ty = T.typeOfBind b+ extend' (w:ws') = (ty:w) : ws'+ extend' [] = [[ty]]+ in liftValue b $ env { witnesses = extend' (witnesses env) }++ | otherwise+ = insertDef b Nothing (liftValue b env)+++-- | Extend an environment with the variables bount by these let-bindings.+--+-- If it's a letregion, remember the region's name and any witnesses.+--+extendLets :: Ord n => Lets a n -> RewriteEnv a n -> RewriteEnv a n+extendLets (LPrivate bs _mt cs) renv+ = foldl (flip extend) (foldl extendB renv bs) cs+ where + extendB (env@RewriteEnv{witnesses = ws, letregions = rs}) b+ = case b of+ BAnon{}+ -> env { witnesses = [] : ws+ , letregions = [b] : rs }+ + BName{}+ -> env { letregions = extend' b rs }++ BNone{}+ -> env++ extend' b (r:rs') = (b:r) : rs'+ extend' b [] = [[b]]++extendLets (LLet b def) env+ = insertDef b (Just def') (liftValue b env)+ where def' = case b of+ BAnon{} -> L.liftX 1 def+ _ -> def++extendLets (LRec bs) env+ = foldl lift' env (map fst bs)+ where lift' e b = insertDef b Nothing (liftValue b e)++++-- Witnesses ------------------------------------------------------------------+-- | Check if the witness map in the given environment.+--- +-- This tries each set in turn, lowering the indices in c by 1 after each+-- unsuccessful match. If nothing matches then 'c' may end up with negative +-- indices, which will definiately not match anything else.+--+containsWitness :: Ord n => Type n -> RewriteEnv a n -> Bool+containsWitness c env+ = go c (witnesses env)+ where go _ [] = False+ go c' (w:ws) = c' `elem` w || go (L.liftT (-1) c') ws+++-- | Get a list of all the witness types in an environment, +-- normalising their indices.+getWitnesses :: Ord n => RewriteEnv a n -> [Type n]+getWitnesses env+ = go (witnesses env) 0+ where go [] _ = []+ go (w:ws) i = map (L.liftT i) w ++ go ws (i+1)+++-- Regions --------------------------------------------------------------------+-- | Check whether an environment contains the given region, +-- bound by a letregion.+containsRegion :: Ord n => Bound n -> RewriteEnv a n -> Bool+containsRegion r env+ = go r (letregions env)+ where + go _ []+ = False++ go (UIx 0) (w:_) + = any (T.boundMatchesBind (UIx 0)) w++ go (UIx n) (_:ws) + = go (UIx (n-1)) ws++ go (UName n) (w:ws) + = any (T.boundMatchesBind (UName n)) w || go r ws++ go (UPrim _ _) _+ = False+++-- Defs -----------------------------------------------------------------------+-- | Insert a rewrite definition into the environment.+insertDef :: Bind n -> Maybe (Exp a n) -> RewriteEnv a n -> RewriteEnv a n+insertDef b def env+ = env { defs = extend' $ defs env }+ where + extend' (r:rs') = ((b,def):r) : rs'+ extend' [] = [[(b,def)]]+++hasDef :: (Ord n, L.MapBoundX (Exp a) n)+ => Bound n -> RewriteEnv a n -> Bool++hasDef b env+ = isJust $ getDef' b env+++-- | Lookup the definition of some let-bound variable from the environment.+getDef :: (Ord n, L.MapBoundX (Exp a) n)+ => Bound n+ -> RewriteEnv a n+ -> Maybe (Exp a n)+getDef b env+ = fromMaybe Nothing $ getDef' b env+++getDef' :: (Ord n, L.MapBoundX (Exp a) n)+ => Bound n+ -> RewriteEnv a n+ -> Maybe (Maybe (Exp a n))++getDef' b env+ = go b 0 (defs env)+ where + go _ _ [] = Nothing+ go b' i (w:ws) = match b' i w `orM` go (L.liftX (-1) b') (i+1) ws++ match b' i ds+ = fmap (fmap $ L.liftX i)+ $ listToMaybe+ $ map snd+ $ filter (T.boundMatchesBind b' . fst) ds++ orM (Just x) _ = Just x+ orM Nothing y = y+++-- | Raise all elements in witness map if binder is anonymous.+-- Only call with type binders: ie XLAM, not XLam+lift :: Bind n -> RewriteEnv a n -> RewriteEnv a n+lift b env@(RewriteEnv ws rs is)+ = case b of+ BAnon{} -> RewriteEnv ([]:ws) ([]:rs) is+ _ -> env+++-- | Raise all elements in definitions map if binder is anonymous+-- Use for *value* binders, not type binders.+liftValue :: Bind n -> RewriteEnv a n -> RewriteEnv a n+liftValue b env@(RewriteEnv ws rs is)+ = case b of+ BAnon{} -> RewriteEnv ws rs ([]:is)+ _ -> env+
+ DDC/Core/Transform/Rewrite/Error.hs view
@@ -0,0 +1,81 @@+module DDC.Core.Transform.Rewrite.Error+ ( Error (..)+ , Side (..))+where+import DDC.Core.Exp+import DDC.Core.Check ()+import DDC.Core.Pretty+import qualified DDC.Core.Check as C+++-- | What can go wrong when checking a rewrite rule.+data Error a n+ -- | Error typechecking one of the expressions+ = ErrorTypeCheck+ { -- | What side of the rule the error was on.+ errorSide :: Side+ , errorExp :: Exp a n+ , errorCheckError :: C.Error a n }++ -- | Error typechecking one of the expressions+ | ErrorBadConstraint+ { errorConstraint :: Type n }++ -- | Types don't match...+ | ErrorTypeConflict+ { errorTypeLhs :: (Type n, Effect n, Closure n)+ , errorTypeRhs :: (Type n, Effect n, Closure n) }++ -- | No binders allowed in left-hand side (right is fine, eg @let@s)+ | ErrorNotFirstOrder+ { errorExp :: Exp a n }++ -- | All variables must be mentioned in left-hand side,+ -- otherwise they won't get bound.+ | ErrorVarUnmentioned++ -- | I don't want to deal with anonymous variables.+ | ErrorAnonymousBinder+ { errorBinder :: Bind n }+++-- | What side of a rewrite rule we're talking about.+data Side + = Lhs | Rhs+++instance Pretty Side where+ ppr Lhs = text "lhs"+ ppr Rhs = text "rhs"+++instance (Pretty a, Pretty n, Show n, Eq n) + => Pretty (Error a n) where+ ppr err+ = case err of+ ErrorTypeCheck s x e+ -> vcat [ text "Can't typecheck " <> ppr s <> text ": " <> ppr e+ , text "While checking " <> ppr x ]++ ErrorBadConstraint c+ -> text "Bad constraint: " <> ppr c++ ErrorTypeConflict (tl,el,cl) (tr,er,cr)+ -> vcat [ text "LHS and RHS have different types:"+ , text "Type L: " <> ppr tl + , text "Type R: " <> ppr tr+ , text "Eff L: " <> ppr el+ , text "Eff R: " <> ppr er+ , text "Clo L: " <> ppr cl+ , text "Clo R: " <> ppr cr ]++ ErrorNotFirstOrder x+ -> vcat [ text "No binders allowed in left-hand side."+ , text "While checking " <> ppr x ]++ ErrorVarUnmentioned + -> text "All variables in rule should be mentioned in left-hand side."++ ErrorAnonymousBinder b+ -> text "Anonymous binders, just give it a name: " <> ppr b+
+ DDC/Core/Transform/Rewrite/Match.hs view
@@ -0,0 +1,213 @@+-- | Create substitution to make (subst template) == target+module DDC.Core.Transform.Rewrite.Match+ ( -- * Substitutions+ SubstInfo+ , emptySubstInfo ++ -- * Matching+ , match)+where+import DDC.Core.Exp+import Data.Set (Set)+import Data.Map (Map)+import qualified DDC.Type.Sum as Sum+import qualified DDC.Type.Transform.AnonymizeT as T+import qualified DDC.Core.Transform.AnonymizeX as T+import qualified DDC.Core.Transform.Reannotate as T+import qualified DDC.Type.Exp.Simple as T+import qualified Data.Map as Map+import qualified Data.Set as Set+import qualified DDC.Core.Env.EnvT as EnvT+++-------------------------------------------------------------------------------+-- | Value and type substition.+type SubstInfo a n + = (Map n (Exp a n), Map n (Type n))++-- | An empty substition info.+emptySubstInfo :: SubstInfo a n+emptySubstInfo + = (Map.empty, Map.empty)++lookupx n (xs,_) + = Map.lookup n xs++insertx n x (xs,tys) + = (Map.insert n x xs, tys)+++-- Match Exp ------------------------------------------------------------------+-- | Create substitution to make (subst template) == target+-- Does not handle higher-order templates (ie ones with binders)+--+-- @ match emptySubstInfo (Set.fromList [r1, r2, s])+-- (stream [r1] (unstream [r2] s))+-- (stream [R0#] (unstream [R1#] (someStream 23))+--+-- => { r1 |-> R0#, r2 |-> R1, s |-> someStream 23 }+-- @+match :: (Show a, Show n, Ord n)+ => SubstInfo a n -- ^ Current substitution+ -> Set n -- ^ Variables we're interested in+ -> Exp a n -- ^ Template expression.+ -> Exp a n -- ^ Target expression.+ -> Maybe (SubstInfo a n)++-- Variables bound by the rule: restricted to just UName earlier.+match m bs (XVar _ (UName n)) r+ | n `Set.member` bs+ -- Check if it's already been matched+ = case lookupx n m of+ Nothing -> return $ insertx n r m+ Just x + -> -- Check if they're equal. Anonymize so names don't matter.+ -- Reannotate so annotations are ignored.+ let x' = T.anonymizeX $ T.reannotate (const ()) x+ r' = T.anonymizeX $ T.reannotate (const ()) r+ in if x' == r'+ then Just m+ else Nothing++match m _ (XVar _ v1) (XVar _ v2)+ | v1 == v2 = Just m++match m _ (XCon _ c1) (XCon _ c2)+ | c1 == c2 = Just m++match m bs (XApp _ x11 x12) (XApp _ x21 x22)+ = do m' <- match m bs x11 x21+ match m' bs x12 x22++match m bs (XCast _ c1 x1) (XCast _ c2 x2)+ | eqCast c1 c2 + = match m bs x1 x2++match (xs, tys) bs (XType _ t1) (XType _ t2)+ = do tys' <- matchT t1 t2 bs tys+ return (xs, tys')++match m _ (XWitness _ w1) (XWitness _ w2)+ | eqWit w1 w2 = return m++match _ _ _ _ + = Nothing+++eqCast :: Ord n => Cast a n -> Cast a n -> Bool+eqCast lc rc + = clean lc == clean rc+ where clean c + = T.reannotate (const ())+ $ case c of+ CastWeakenEffect eff -> CastWeakenEffect $ T.anonymizeT eff+ CastPurify wit -> CastPurify wit+ CastBox -> CastBox+ CastRun -> CastRun+++eqWit :: Ord n => Witness a n -> Witness a n -> Bool+eqWit lw rw + = T.reannotate (const ()) lw + == T.reannotate (const ()) rw+++-- Types ----------------------------------------------------------------------+type VarSet n = Set.Set n+type Subst n = Map.Map n (Type n)+++-- | Try to find a simple substitution between two types.+-- Ignoring complicated effect sums.+-- Also ignoring TForall - checkRewriteRule outlaws foralls in the template, so it's safe.+-- Eg given template @a -> b@ and target @Int -> Float@,+-- returns substitution:+-- @{ a |-> Int, b |-> Float }@+--+matchT :: Ord n+ => Type n -- ^ Template type.+ -> Type n -- ^ Target type.+ -> VarSet n -- ^ Only attempt to match these names.+ -> Subst n -- ^ Already matched (or @Map.empty@)+ -> Maybe (Subst n)++matchT t1 t2 vs subst+ = let t1' = unpackSumT $ T.crushSomeT EnvT.empty t1+ t2' = unpackSumT $ T.crushSomeT EnvT.empty t2+ in case (t1', t2') of+ -- Constructor names must be equal.+ --+ -- Will this still work when it's a TyConBound - basically same as TVar?+ (TCon tc1, TCon tc2)+ | tc1 == tc2+ -> Just subst++ -- Decend into applications.+ (TApp t11 t12, TApp t21 t22)+ -> matchT t11 t21 vs subst >>= matchT t12 t22 vs++ -- Sums are equivalent if all of their components are.+ -- Very simple matching, only consider equivalent if both have same+ -- length and in the same order.+ --+ -- > (Read + Write + a) `matchT` (Read + Write + Alloc)+ -- > =+ -- > Just { a |-> Alloc }+ -- but+ -- > (Read + a + Write) `matchT` (Read + Write + Alloc)+ -- > =+ -- > Nothing+ -- and+ -- > (Read + Write + Alloc + a) `matchT` (Read + Write + Alloc)+ -- > =+ -- > Nothing+ -- despite a valid substitution existing as+ -- > { a |-> !0 }+ --+ (TSum ts1, TSum ts2)+ -> let ts1' = Sum.toList ts1+ ts2' = Sum.toList ts2++ go (l:ls) (r:rs) s = matchT l r vs s >>= go ls rs+ go _ _ s = Just s+ in if length ts1' /= length ts2'+ then Nothing+ else go ts1' ts2' subst+++ -- If template is in variable set, push the target into substitution+ (TVar (UName n), _)+ | Set.member n vs+ , Nothing <- Map.lookup n subst+ -> Just $ Map.insert n t2' subst++ | Set.member n vs+ , Just t1'' <- Map.lookup n subst+ , T.equivT EnvT.empty t1'' t2'+ -> Just subst++ -- Both are variables but it's not a template variable,+ -- so it's only valid if they're equal.+ (TVar (UName n), TVar v2)+ | not $ Set.member n vs+ , UName n == v2+ -> Just subst++ (TVar (UIx i), TVar v2)+ | UIx i == v2+ -> Just subst++ (TVar (UPrim n t), TVar v2)+ | UPrim n t == v2+ -> Just subst++ -- Otherwise the two are different+ (_, _) -> Nothing+++-- | Unpack single element sums into plain types.+unpackSumT :: Type n -> Type n+unpackSumT (TSum ts)+ | [t] <- Sum.toList ts = t+unpackSumT tt = tt+
+ DDC/Core/Transform/Rewrite/Parser.hs view
@@ -0,0 +1,130 @@++-- | Core language parser.+module DDC.Core.Transform.Rewrite.Parser+ (pRule, pRuleMany)+where+import DDC.Core.Exp+import DDC.Core.Parser+import DDC.Core.Lexer.Tokens+import qualified DDC.Control.Parser as P+import qualified DDC.Type.Exp.Simple as T+import qualified DDC.Core.Transform.Rewrite.Rule as R+++-- Rewrite Rules ----------------------------------------------------------------+{-+ [r1 r2 : %] (x : Int r1).+ Const r1 =>+ addInt [:r1 r2 r1:] x (0 [r2] ()) =+ x+-}+-- | Parse a rewrite rule.+pRule :: Ord n + => Context n -> Parser n (R.RewriteRule P.SourcePos n)+pRule c+ = do bs <- pRuleBinders c+ (cs,lhs) <- pRuleCsLhs c+ hole <- pRuleHole c+ pSym SEquals+ rhs <- pExp c++ return $ R.mkRewriteRule bs cs lhs hole rhs+++{-+add_zero_r+ [r1 r2 : %] (x : Int r1).+ Const r1 =>+ addInt [:r1 r2 r1:] x (0 [r2] ()) =+ x;+add_zero_l+ [r1 r2 : %] ...+ ;+-}+-- | Parse many rewrite rules.+pRuleMany + :: Ord n + => Context n -> Parser n [(n,R.RewriteRule P.SourcePos n)]+pRuleMany c+ = P.many (do+ n <- pName+ r <- pRule c+ pSym SSemiColon+ return (n,r))+++pRuleBinders + :: Ord n + => Context n -> Parser n [(R.BindMode,Bind n)]++pRuleBinders c+ = P.choice+ [ do bs <- P.many1 (pBinders c)+ pSym SDot+ return $ concat bs+ , return []+ ]+++pRuleCsLhs + :: Ord n + => Context n -> Parser n ([Type n], Exp P.SourcePos n)+pRuleCsLhs c+ = P.choice+ [ do cs <- P.many1 $ P.try (do+ cc <- pTypeApp c+ pSym SArrowEquals+ return cc)+ lhs <- pExp c+ return (cs,lhs)+ , do lhs <- pExp c+ return ([],lhs)+ ]+++pRuleHole + :: Ord n + => Context n -> Parser n (Maybe (Exp P.SourcePos n))+pRuleHole c+ = P.optionMaybe+ $ do pSym SUnderscore+ pSym SBraceBra+ e <- pExp c+ pSym SBraceKet+ pSym SUnderscore+ return e+++-- | Parse rewrite binders+--+-- Many of:+-- [BIND1 BIND2 .. BINDN : TYPE]+-- or (BIND : TYPE)+--+pBinders + :: Ord n + => Context n -> Parser n [(R.BindMode, Bind n)]+pBinders c+ = P.choice+ [ pBindersBetween c R.BMSpec (pSym SSquareBra) (pSym SSquareKet)+ , pBindersBetween c (R.BMValue 0) (pSym SRoundBra) (pSym SRoundKet)+ ]+++pBindersBetween + :: Ord n + => Context n+ -> R.BindMode + -> Parser n a + -> Parser n a+ -> Parser n [(R.BindMode,Bind n)]++pBindersBetween c bm bra ket+ = do bra+ bs <- P.many1 pBinder+ pTok (KOp ":")+ t <- pType c+ ket+ return $ map (mk t) bs+ where mk t b = (bm,T.makeBindFromBinder b t)+
+ DDC/Core/Transform/Rewrite/Rule.hs view
@@ -0,0 +1,511 @@+-- | Constructing and checking whether rewrite rules are valid+module DDC.Core.Transform.Rewrite.Rule + ( -- * Binding modes+ BindMode (..)+ , isBMSpec+ , isBMValue++ , RewriteRule (..)+ , NamedRewriteRule++ -- * Construction+ , mkRewriteRule+ , checkRewriteRule+ , Error (..)+ , Side (..))+where+import DDC.Core.Transform.Rewrite.Error+import DDC.Core.Transform.Reannotate+import DDC.Core.Transform.TransformUpX+import DDC.Core.Exp.Annot+import DDC.Core.Pretty ()+import DDC.Core.Collect+import DDC.Core.Pretty ()+import DDC.Core.Env.EnvX (EnvX)+import DDC.Data.Pretty+import qualified DDC.Core.Analysis.Usage as U+import qualified DDC.Core.Check as C+import qualified DDC.Core.Collect as C+import qualified DDC.Core.Transform.SpreadX as S+import qualified DDC.Type.Env as T+import qualified DDC.Type.Exp.Simple as T+import qualified DDC.Type.Transform.SpreadT as S+import qualified Data.Map as Map+import qualified Data.Maybe as Maybe+import qualified Data.Set as Set+import qualified DDC.Type.Env as Env+import qualified DDC.Core.Env.EnvT as EnvT+import qualified DDC.Core.Env.EnvX as EnvX++-- | A rewrite rule. For example:+--+-- @ RULE [r1 r2 r3 : %] (x : Int r1)+-- . addInt [:r1 r2 r3:] x (0 [r2] ()+-- = copyInt [:r1 r3:] x+-- @+data RewriteRule a n+ = RewriteRule+ { -- | Variables bound by the rule.+ ruleBinds :: [(BindMode, Bind n)]++ -- | Extra constraints on the rule.+ -- These must all be satisfied for the rule to fire.+ , ruleConstraints :: [Type n] ++ -- | Left-hand side of the rule.+ -- We match on this part.+ , ruleLeft :: Exp a n ++ -- | Extra part of left-hand side,+ -- but allow this bit to be out-of-context.+ , ruleLeftHole :: Maybe (Exp a n) ++ -- | Right-hand side of the rule.+ -- We replace the matched expression with this part.+ , ruleRight :: Exp a n ++ -- | Effects that are caused by the left but not the right.+ -- When applying the rule we add an effect weakning to ensure+ -- the rewritten expression has the same effects.+ , ruleWeakEff :: Maybe (Effect n) ++ -- | Closure that the left has that is not present in the right.+ -- When applying the rule we add a closure weakening to ensure+ -- the rewritten expression has the same closure.+ , ruleWeakClo :: [Exp a n] ++ -- | References to environment. + -- Used to check whether the rule is shadowed.+ , ruleFreeVars :: [Bound n] + } deriving (Eq, Show)+++type NamedRewriteRule a n+ = (String, RewriteRule a n)+++instance (Pretty n, Eq n) => Pretty (RewriteRule a n) where+ ppr (RewriteRule bs cs lhs hole rhs _ _ _)+ = pprBinders bs <> pprConstrs cs <> ppr lhs <> pprHole <> text " = " <> ppr rhs+ where pprBinders [] = text ""+ pprBinders bs' = foldl1 (<>) (map pprBinder bs') <> text ". "++ pprBinder (BMSpec, b) = text "[" <> ppr b <> text "] "+ pprBinder (BMValue _, b) = text "(" <> ppr b <> text ") "+ + pprConstrs [] = text ""+ pprConstrs (c:cs') = ppr c <> text " => " <> pprConstrs cs'++ pprHole+ | Just h <- hole+ = text " {" <> ppr h <> text "}"++ | otherwise+ = text ""+++-- BindMode -------------------------------------------------------------------+-- | Binding level for the binders in a rewrite rule.+data BindMode + -- | Level-1 binder (specs)+ = BMSpec ++ -- | Level-0 binder (data values and witnesses)+ | BMValue Int -- ^ number of usages+ deriving (Eq, Show)+++-- | Check if a `BindMode` is a `BMSpec`.+isBMSpec :: BindMode -> Bool+isBMSpec BMSpec = True+isBMSpec _ = False+++-- | Check if a `BindMode` is a `BMValue`.+isBMValue :: BindMode -> Bool+isBMValue (BMValue _) = True+isBMValue _ = False+++-- Make -----------------------------------------------------------------------+-- | Construct a rewrite rule, but do not check if it's valid.+--+-- You then need to apply 'checkRewriteRule' to check it.+--+mkRewriteRule+ :: [(BindMode,Bind n)] -- ^ Variables bound by the rule.+ -> [Type n] -- ^ Extra constraints on the rule.+ -> Exp a n -- ^ Left-hand side of the rule.+ -> Maybe (Exp a n) -- ^ Extra part of left, can be out of context.+ -> Exp a n -- ^ Right-hand side (replacement)+ -> RewriteRule a n++mkRewriteRule bs cs lhs hole rhs+ = RewriteRule bs cs lhs hole rhs Nothing [] []+++-- Check ----------------------------------------------------------------------+-- | Take a rule, make sure it's valid and fill in type, closure and effect+-- information.+--+-- The left-hand side of rule can't have any binders (lambdas, lets etc).+--+-- All binders must appear in the left-hand side, otherwise they would match+-- with no value.+--+-- Both sides must have the same types, but the right can have fewer effects+-- and smaller closure.+--+-- We don't handle anonymous binders on either the left or right.+--+checkRewriteRule+ :: (Show a, Ord n, Show n, Pretty n) + => C.Config n -- ^ Type checker config.+ -> EnvX n -- ^ Type checker environment.+ -> RewriteRule a n -- ^ Rule to check+ -> Either (Error a n)+ (RewriteRule (C.AnTEC a n) n)++checkRewriteRule config env+ (RewriteRule bs cs lhs hole rhs _ _ _)+ = do + -- Extend the environments with variables bound by the rule.+ let (env', bs') = extendBinds bs env+ let kenv' = EnvX.kindEnvOfEnvX env'+ let csSpread = map (S.spreadT kenv') cs++ -- Check that all constraints are valid types.+ mapM_ (checkConstraint config) csSpread++ -- Typecheck, spread and annotate with type information+ (lhs', _, _)+ <- checkExp config env' Lhs lhs ++ -- If the extra left part is there, typecheck and annotate it.+ hole' <- case hole of+ Just h + -> do (h',_,_) <- checkExp config env' Lhs h + return $ Just h'++ Nothing -> return Nothing++ -- Build application from lhs and the hole so we can check its+ -- type against rhs+ let a = annotOfExp lhs+ let lhs_full = maybe lhs (XApp a lhs) hole++ -- Check the full left hand side.+ (lhs_full', tLeft, effLeft)+ <- checkExp config env' Lhs lhs_full++ -- Check the full right hand side.+ (rhs', tRight, effRight)+ <- checkExp config env' Rhs rhs ++ -- Check that types of both sides are equivalent.+ let err = ErrorTypeConflict + (tLeft, effLeft, tBot kClosure) + (tRight, effRight, tBot kClosure)++ checkEquiv tLeft tRight err++ -- Check the effect of the right is smaller than that + -- of the left, and add a weakeff cast if nessesary+ effWeak <- makeEffectWeakening T.kEffect effLeft effRight err++ -- Check that the closure of the right is smaller than that+ -- of the left, and add a weakclo cast if nessesary.+ cloWeak <- makeClosureWeakening config env' lhs_full' rhs'++ -- Check that all the bound variables are mentioned+ -- in the left-hand side.+ checkUnmentionedBinders bs' lhs_full'++ -- No BAnons allowed.+ -- We don't handle deBruijn binders.+ checkAnonymousBinders bs'++ -- No lets or lambdas in left-hand side.+ -- We can't match against these.+ checkValidPattern lhs_full++ -- Count how many times each binder is used in the right-hand side.+ bs'' <- countBinderUsage bs' rhs++ -- Get the free variables of the rule.+ let binds = Set.fromList+ $ Maybe.catMaybes+ $ map (T.takeSubstBoundOfBind . snd) bs++ let freeVars = Set.toList+ $ (C.freeX T.empty lhs_full' + `Set.union` C.freeX T.empty rhs)+ `Set.difference` binds++ return $ RewriteRule + bs'' csSpread+ lhs' hole' rhs'+ effWeak cloWeak+ freeVars+++-- | Extend kind and type environments with a rule's binders.+-- Which environment a binder goes into depends on its BindMode.+-- Also return list of binders which have been spread.+extendBinds + :: Ord n + => [(BindMode, Bind n)] + -> EnvX n+ -> (EnvX n, [(BindMode, Bind n)])++extendBinds binds env0+ = go binds env0 []+ where+ go [] env acc+ = (env, acc)++ go ((bm,b):bs) env acc+ = let kenv = EnvX.kindEnvOfEnvX env+ tenv = EnvX.typeEnvOfEnvX env+ b' = S.spreadX kenv tenv b+ env' = case bm of+ BMSpec -> EnvX.extendT b' env+ BMValue _ -> EnvX.extendX b' env++ in go bs env' (acc ++ [(bm,b')])+++-- | Type check the expression on one side of the rule.+checkExp + :: (Show a, Ord n, Show n, Pretty n)+ => C.Config n -- ^ Type checker config.+ -> EnvX n -- ^ Type checker environment.+ -> Side -- ^ Side that the expression appears on for errors.+ -> Exp a n -- ^ Expression to check.+ -> Either (Error a n) + (Exp (C.AnTEC a n) n, Type n, Effect n)++checkExp defs env side xx+ = let kenv = EnvX.kindEnvOfEnvX env+ tenv = EnvX.typeEnvOfEnvX env+ xx' = S.spreadX kenv tenv xx ++ in case fst $ C.checkExp defs env C.Recon C.DemandNone xx' of+ Left err -> Left $ ErrorTypeCheck side xx' err+ Right rhs -> return rhs+++-- | Type check a constraint on the rule.+checkConstraint+ :: (Ord n, Show n, Pretty n)+ => C.Config n+ -> Type n -- ^ The constraint type to check.+ -> Either (Error a n) (Kind n)++checkConstraint config tt+ = case C.checkSpec config tt of+ Left _err -> Left $ ErrorBadConstraint tt+ Right (_, k)+ | T.isWitnessType tt -> return k+ | otherwise -> Left $ ErrorBadConstraint tt+++-- | Check equivalence of types or error+checkEquiv+ :: Ord n+ => Type n -- ^ Type of left of rule.+ -> Type n -- ^ Type of right of rule.+ -> Error a n -- ^ Error to report if the types don't match.+ -> Either (Error a n) ()++checkEquiv tLeft tRight err+ | T.equivT EnvT.empty tLeft tRight + = return ()+ | otherwise = Left err+++-- Weaken ---------------------------------------------------------------------+-- | Make the effect weakening for a rule.+-- This contains the effects that are caused by the left of the rule+-- but not the right. +-- If the right has more effects than the left then return an error.+--+makeEffectWeakening+ :: Ord n+ => Kind n -- ^ Should be the effect kind.+ -> Effect n -- ^ Effect of the left of the rule.+ -> Effect n -- ^ Effect of the right of the rule.+ -> Error a n -- ^ Error to report if the right is bigger.+ -> Either (Error a n) (Maybe (Type n))++makeEffectWeakening k effLeft effRight onError+ -- When the effect of the left matches that of the right+ -- then we don't have to do anything else.+ | T.equivT EnvT.empty effLeft effRight+ = return Nothing++ -- When the effect of the right is smaller than that of+ -- the left then we need to wrap it in an effect weaking+ -- so the rewritten expression retains its original effect.+ | T.subsumesT EnvT.empty k effLeft effRight+ = return $ Just effLeft++ -- When the effect of the right is more than that of the left+ -- then this is an error. The rewritten expression can't have+ -- can't have more effects than the source.+ | otherwise+ = Left onError+++-- | Make the closure weakening for a rule.+-- This contains a closure term for all variables that are present+-- in the left of a rule but not in the right.+--+makeClosureWeakening + :: (Ord n, Pretty n, Show n)+ => C.Config n -- ^ Type-checker config+ -> EnvX n -- ^ Type checker environment.+ -> Exp (C.AnTEC a n) n -- ^ Expression on the left of the rule.+ -> Exp (C.AnTEC a n) n -- ^ Expression on the right of the rule.+ -> Either (Error a n) + [Exp (C.AnTEC a n) n]++makeClosureWeakening config env lhs rhs+ = let lhs' = removeEffects config env lhs+ supportLeft = support Env.empty Env.empty lhs'+ daLeft = supportDaVar supportLeft+ wiLeft = supportWiVar supportLeft+ spLeft = supportSpVar supportLeft++ rhs' = removeEffects config env rhs+ supportRight = support Env.empty Env.empty rhs'+ daRight = supportDaVar supportRight+ wiRight = supportWiVar supportRight+ spRight = supportSpVar supportRight++ a = annotOfExp lhs++ in Right + $ [XVar a u + | u <- Set.toList $ daLeft `Set.difference` daRight ]++ ++ [XWitness a (WVar a u)+ | u <- Set.toList $ wiLeft `Set.difference` wiRight ]++ ++ [XType a (TVar u)+ | u <- Set.toList $ spLeft `Set.difference` spRight ]+++-- | Replace all effects with !0.+-- This is done so that when @makeClosureWeakening@ finds free variables,+-- it ignores those only mentioned in effects.+removeEffects+ :: (Ord n, Pretty n, Show n)+ => C.Config n -- ^ Type-checker config+ -> EnvX n -- ^ Type checker environment.+ -> Exp a n -- ^ Target expression - has all effects replaced with bottom.+ -> Exp a n++removeEffects config + = transformUpX remove+ where+ remove _env x++ | XType a et <- x+ , Right (_, k) <- C.checkSpec config et+ , T.isEffectKind k+ = XType a $ T.tBot T.kEffect++ | otherwise+ = x+++-- Structural Checks ----------------------------------------------------------+-- | Check for rule variables that have no uses.+checkUnmentionedBinders+ :: Ord n+ => [(BindMode, Bind n)]+ -> Exp (C.AnTEC a n) n+ -> Either (Error a n) ()++checkUnmentionedBinders bs expr+ = let used = C.freeX T.empty expr `Set.union` C.freeT T.empty expr++ binds = Set.fromList+ $ Maybe.catMaybes+ $ map (T.takeSubstBoundOfBind . snd) bs++ in if binds `Set.isSubsetOf` used+ then return ()+ else Left ErrorVarUnmentioned+++-- | Check for anonymous binders in the rule. We don't handle these.+checkAnonymousBinders + :: [(BindMode, Bind n)] + -> Either (Error a n) ()++checkAnonymousBinders bs+ | (b:_) <- filter T.isBAnon $ map snd bs+ = Left $ ErrorAnonymousBinder b++ | otherwise+ = return ()+++-- | Check whether the form of the left-hand side of the rule is valid+-- we can only match against nested applications, and not general+-- expressions containing let-bindings and the like.+checkValidPattern :: Exp a n -> Either (Error a n) ()+checkValidPattern expr+ = go expr+ where go (XVar _ _) = return ()+ go (XCon _ _) = return ()+ go x@(XLAM _ _ _) = Left $ ErrorNotFirstOrder x+ go x@(XLam _ _ _) = Left $ ErrorNotFirstOrder x+ go (XApp _ l r) = go l >> go r+ go x@(XLet _ _ _) = Left $ ErrorNotFirstOrder x+ go x@(XCase _ _ _) = Left $ ErrorNotFirstOrder x+ go (XCast _ _ x) = go x+ go (XType a t) = go_t a t+ go (XWitness _ _) = return ()++ go_t _ (TVar _) = return ()+ go_t _ (TCon _) = return ()+ go_t a t@(TAbs _ _) = Left $ ErrorNotFirstOrder (XType a t)+ go_t a (TApp l r) = go_t a l >> go_t a r+ go_t a t@(TForall _ _) = Left $ ErrorNotFirstOrder (XType a t)+ go_t _ (TSum _) = return ()+++-- | Count how many times each binder is used in right-hand side.+countBinderUsage + :: Ord n + => [(BindMode, Bind n)] + -> Exp a n + -> Either (Error a n) [(BindMode, Bind n)]++countBinderUsage bs x+ = let U.UsedMap um+ = fst $ annotOfExp $ U.usageX x++ get (BMValue _, BName n t)+ = (BMValue+ $ length+ $ Maybe.fromMaybe []+ $ Map.lookup n um+ , BName n t)++ get b+ = b++ in return $ map get bs+++-- | Allow the expressions and anything else with annotations to be reannotated+instance Reannotate RewriteRule where+ reannotate f (RewriteRule bs cs lhs hole rhs eff clo fv)+ = RewriteRule bs cs (re lhs) (fmap re hole) (re rhs) eff (map re clo) fv+ where+ re = reannotate f+
+ DDC/Core/Transform/Snip.hs view
@@ -0,0 +1,350 @@++-- | Snip out nested applications.+module DDC.Core.Transform.Snip+ ( Snip (..)+ , Config (..)+ , configZero)+where+import DDC.Core.Analysis.Arity+import DDC.Core.Module+import DDC.Core.Exp.Annot+import qualified DDC.Core.Transform.BoundX as L+import qualified DDC.Type.Exp.Simple as T+++-------------------------------------------------------------------------------+-- | Snipper configuration.+data Config + = Config+ { -- | Introduce new bindings for over-applied functions.+ configSnipOverApplied :: Bool+ + -- | Ensure the body of a let-expression is a variable.+ , configSnipLetBody :: Bool++ -- | Treat lambda abstractions as atomic, + -- and don't snip them.+ , configPreserveLambdas :: Bool + }+++-- | Snipper configuration with all flags set to False.+configZero :: Config+configZero+ = Config+ { configSnipOverApplied = False+ , configSnipLetBody = False + , configPreserveLambdas = False }+++-------------------------------------------------------------------------------+-- | Class of things that can have things snipped out of them.+class Snip (c :: * -> *) where++ -- | Snip out nested applications as anonymous bindings.+ -- + -- @+ -- f (g x) (h y)+ -- ==> let ^ = g x in ^ = h y in f ^1 ^0+ -- @+ snip :: Ord n => Config -> c n -> c n+++instance Snip (Module a) where+ snip config mm+ = let arities = aritiesOfModule mm+ body' = snipX config arities (moduleBody mm) []+ in mm { moduleBody = body' }+++instance Snip (Exp a) where+ snip config x + = snipX config emptyArities x []+++-- | Convert an expression into A-normal form.+snipX :: Ord n+ => Config+ -> Arities n -- ^ Arities of functions in environment.+ -> Exp a n -- ^ Expression to transform.+ -> [(Exp a n, a)] -- ^ Arguments being applied to current expression.+ -> Exp a n++snipX config arities x args+ -- For applications, remember the argument that the function is being + -- applied to, and decend into the function part.+ -- This unzips application nodes as we decend into the tree.+ | XApp a fun arg <- x+ = snipX config arities fun + $ (snipX config arities arg [], a) : args++ -- Some non-application node with no arguments.+ | null args+ = enterX config arities x++ -- Some non-application node being applied to arguments.+ | otherwise+ = let x' = enterX config arities x+ in buildNormalisedApp config arities x' args++-- Enter into a non-application.+enterX config arities xx+ = let down ars e + = snipX config (extendsArities arities ars) e []++ in case xx of+ -- The snipX function shouldn't have called us with an XApp.+ XApp{} + -> error "ddc-core-simpl.Snip: snipX shouldn't give us an XApp"++ -- leafy constructors+ XVar{} -> xx+ XCon{} -> xx+ XType{} -> xx+ XWitness{} -> xx++ -- lambdas+ XLAM a b e+ -> XLAM a b (down [(b,0)] e)++ XLam a b e+ -> XLam a b (down [(b,0)] e)++ -- non-recursive let+ XLet a (LLet b x1) x2+ -> let x1' = down [] x1+ x2' = snipLetBody config a+ $ down [(b, arityOfExp' x1')] x2+ in XLet a (LLet b x1') x2'++ -- recursive let+ XLet a (LRec lets) x2+ -> let bs = map fst lets + xs = map snd lets + ars = zip bs (map arityOfExp' xs) + xs' = map (down ars) xs+ x2' = snipLetBody config a $ down ars x2+ in XLet a (LRec $ zip bs xs') x2' ++ -- private, just make sure we record bindings with dummy val.+ XLet a (LPrivate b mt bs) x2+ -> let ars = zip bs (repeat 0)+ x2' = snipLetBody config a $ down ars x2+ in XLet a (LPrivate b mt bs) x2'++ -- case+ -- Split out non-atomic discriminants into their own bindings.+ XCase a e alts+ | isAtom e+ -> let e' = down [] e + alts' = map (\(AAlt pat ae) + -> AAlt pat (down (aritiesOfPat pat) ae)) alts + in XCase a e' alts'++ | otherwise+ -> let e' = down [] e+ alts' = [AAlt pat (down (aritiesOfPat pat) ae) + | AAlt pat ae <- alts]+ xBody' = snipLetBody config a+ $ XCase a (XVar a $ UIx 0)+ (map (L.liftX 1) alts')++ in XLet a (LLet (BAnon (T.tBot T.kData)) e')+ xBody'++ -- cast+ XCast a c e+ -> XCast a c (down [] e)+++-- | Build an A-normalised application of some functional expression to +-- its arguments. Atomic arguments are applied directly, while +-- non-atomic arguments are bound via let-expressions, then the+-- associated let-bound variable is passed to the function.+buildNormalisedApp + :: Ord n+ => Config -- ^ Snipper config.+ -> Arities n -- ^ environment, arities of bound variables+ -> Exp a n -- ^ function+ -> [(Exp a n,a)] -- ^ arguments being applied to current expression+ -> Exp a n++buildNormalisedApp _ _ f0 [] = f0+buildNormalisedApp config arities f0 args@( (_, annot) : _)+ = make annot f0 args+ where+ tBot' = T.tBot T.kData++ -- Lookup the arity of the function.+ f0Arity + = case f0 of+ XVar _ b+ | Just arity <- getArity arities b+ -> max arity 1++ _ -> max (arityOfExp' f0) 1++ -- Make a normalised function application.+ make a xFun xsArgs++ -- The function part is already atomic.+ | isAtom xFun+ = buildNormalisedFunApp config a f0Arity xFun xsArgs++ -- The function part is not atomic, + -- so we need to add an outer-most let-binding for it.+ | otherwise+ = XLet a (LLet (BAnon tBot') xFun)+ (snipLetBody config a+ $ buildNormalisedFunApp config a f0Arity + (XVar a (UIx 0)) + [ (L.liftX 1 x, a') | (x, a') <- xsArgs])+++-- | Build an A-normalised application of some functional expression to +-- its arguments. Atomic arguments are applied directly, while +-- on-atomic arguments are bound via let-expressions, then the+-- associated let-bound variable is passed to the function.+--+-- Unlike the `buildNormalisedFunApp` function above, this one+-- wants the function part to be normalised as well.+buildNormalisedFunApp+ :: Config -- ^ Snipper configuration.+ -> a -- ^ Annotation to use.+ -> Int -- ^ Arity of the function part.+ -> Exp a n -- ^ Function part.+ -> [(Exp a n, a)] -- ^ Arguments to apply+ -> Exp a n++buildNormalisedFunApp config an funArity xFun xsArgs+ = let tBot' = T.tBot T.kData++ -- Split arguments into the already atomic ones,+ -- and the ones we need to introduce let-expressions for.+ argss = splitArgs config xsArgs++ -- Collect up the new let-bindings.+ xsLets = [ (x, a) + | (_, a, _, Just x) <- argss]++ -- The total number of new let-bindings.+ nLets = length xsLets++ -- Lift indices in each binding over the bindings before it.+ xsLets' = [ (L.liftX n x, a)+ | (x, a) <- xsLets+ | (n :: Int) <- [0..] ]++ -- Lift indices in the function over the bindings before it.+ xFun' = L.liftX nLets xFun++ -- Collect up the new function arguments.+ -- If the argument was already atomic then we have to lift+ -- its indices past the new let bindings we're about to add.+ -- Otherwise it's a reference to one of the bindings directly.+ xsArgs' = [if liftMe + then (L.liftX nLets xArg, a)+ else (xArg, a)+ | (xArg, a, liftMe, _) <- argss]++ -- Construct the new function application.+ xFunApps ++ -- If the function is over-applied then create an intermediate+ -- binding that saturates it, then apply the extra arguments+ -- separately.+ | configSnipOverApplied config+ , length xsArgs' > funArity+ , (xsSat, xsOver) <- splitAt funArity xsArgs'+ = XLet an (LLet (BAnon tBot') + (makeXAppsWithAnnots xFun' xsSat))+ (snipLetBody config an+ $ makeXAppsWithAnnots + (XVar an (UIx 0)) + [ (L.liftX 1 x, a) | (x, a) <- xsOver ])++ -- Function has the correct number of arguments,+ -- or is partially applied.+ | otherwise+ = makeXAppsWithAnnots + xFun'+ xsArgs' ++ -- Wrap the function application in the let-bindings+ -- for its arguments.+ in case xsLets' of+ [] -> xFunApps+ _ -> foldr (\(x, a) x' -> XLet a x x')+ (snipLetBody config an xFunApps)+ [ (LLet (BAnon tBot') x, a) + | (x, a) <- xsLets' ]+++-- | Sort function arguments into either the atomic ones, +-- or compound ones.+splitArgs + :: Config + -> [(Exp a n, a)] + -> [( Exp a n -- Expression to use as the new argument.+ , a -- Annoation for the argument application.+ , Bool -- Whether this argument was already atomic.+ , Maybe (Exp a n))] -- New expression to let-bind.++splitArgs config args+ = reverse $ go 0 $ reverse args+ where + go _n [] = []+ go n ((xArg, a) : xsArgs)+ | isAtom xArg+ = (xArg, a, True, Nothing) : go n xsArgs++ | configPreserveLambdas config+ , isXLam xArg || isXLAM xArg+ = (xArg, a, True, Nothing) : go n xsArgs++ | otherwise+ = (XVar a (UIx n), a, False, Just xArg) : go (n + 1) xsArgs+++-- | If `snipLetResults` is set and this is not an atomic expression then+-- introduce a new binding for it.+snipLetBody :: Config -> a -> Exp a n -> Exp a n+snipLetBody config a xx+ | configSnipLetBody config+ , not (isAtom xx)+ , not (isXLet xx)+ = let tBot' = T.tBot T.kData+ in XLet a (LLet (BAnon tBot') xx)+ (XVar a (UIx 0))+ + | otherwise+ = xx+++-- | Check if an expression needs a binding, or if it's simple enough to be+-- applied as-is.+isAtom :: Exp a n -> Bool+isAtom xx+ = case xx of+ XVar{} -> True+ XCon{} -> True+ XType{} -> True+ XWitness{} -> True++ -- Keep applications of variables to their types together.+-- XApp _ x1 XType{} -> isAtom x1++ -- Casts are ignored by code generator, so we can leave them in if+ -- their subexpression is normal+ XCast _ _ x -> isAtom x+ _ -> False+++-- | Take the arity of an expression, +-- returning 0 for XType and XWitness.+arityOfExp' :: Ord n => Exp a n -> Int+arityOfExp' xx+ = case arityOfExp xx of+ Nothing -> 0+ Just a -> a+
+ DDC/Core/Transform/Thread.hs view
@@ -0,0 +1,413 @@++-- | Thread a state token through calls to given functions.+--+-- ASSUMPTIONS:+-- * Program is a-normalized and fully named.+--+module DDC.Core.Transform.Thread+ ( Thread (..)+ , Config (..)+ , injectStateType)+where+import DDC.Core.Module+import DDC.Core.Exp.Annot+import DDC.Data.Pretty+import DDC.Core.Transform.Reannotate+import DDC.Core.Check (AnTEC (..))+import DDC.Type.Env (KindEnv, TypeEnv)+import qualified DDC.Type.Env as Env+import qualified DDC.Core.Check as Check+++-------------------------------------------------------------------------------+-- | Configuration for the Thread transform.+data Config a n+ = Config+ { -- | Config for the type checker.+ -- We need to reconstruct the type of the result of stateful+ -- functions when bundling them into the tuple that holds the + -- state token.+ configCheckConfig :: Check.Config n++ -- | Function to decide which top-level bindings are stateful and+ -- need the state token threaded through them. If the binding with+ -- the given name is stateful then the function should return the+ -- new type for the binding that accepts and returns the state token.+ , configThreadMe :: n -> Type n -> Maybe (Type n) ++ -- | Type of the state token to use.+ , configTokenType :: Type n++ -- | Type that represents a missing value.+ -- If a stateful function returns a void then our thread transform+ -- rewrites it to return the state token, instead of a tuple+ -- that contains the token as well as a void value.+ , configVoidType :: Type n++ -- | Wrap a type with the world token.+ -- eg change Int to (World#, Int)+ , configWrapResultType :: Type n -> Type n++ -- | Wrap a result expression with the state token.+ -- The function is given the types of the world token and result,+ -- then the expressions for the same.+ , configWrapResultExp :: Exp (AnTEC a n) n -> Exp (AnTEC a n) n + -> Exp a n++ -- | Make a pattern which binds the world argument+ -- from a threaded primop.+ , configThreadPat :: n -> Maybe (Bind n -> [Bind n] -> Pat n)+ }+++-- | Class of things that can have a state token threaded through them.+class Thread (c :: * -> * -> *) where+ thread :: (Ord n, Show n, Pretty n)+ => Config a n + -> KindEnv n -> TypeEnv n + -> c (AnTEC a n) n + -> c a n+++instance Thread Module where+ thread config kenv tenv mm+ = let body' = threadModuleBody config kenv tenv (moduleBody mm) + in mm { moduleBody = body' }+++-- | Keeps track of which recursive functions we're inside.+data Context n+ -- | We're in the body of an effectful recursive function.+ = ContextRec n++ -- | This effectful function in the context had a world token threaded+ -- through it, but we're not in its body.+ | ContextFun n+ deriving Eq+++-- Module ---------------------------------------------------------------------+-- | Thread state token though a module body.+-- We assume every top-level binding is a stateful function+-- that needs to accept and return the state token.+threadModuleBody + :: (Ord n, Show n, Pretty n)+ => Config a n + -> KindEnv n -> TypeEnv n+ -> Exp (AnTEC a n) n + -> Exp a n++threadModuleBody config kenv tenv xx+ = case xx of+ XLet a lts x+ -> let lts' = threadTopLets config kenv tenv lts+ (bks, bts) = bindsOfLets lts+ kenv' = Env.extends bks kenv+ tenv' = Env.extends bts tenv+ x' = threadModuleBody config kenv' tenv' x+ in XLet (annotTail a) lts' x'++ _ -> reannotate annotTail xx+++-- | Thread state token through some top-level bindings in a module.+threadTopLets + :: (Ord n, Show n, Pretty n)+ => Config a n + -> KindEnv n -> TypeEnv n+ -> Lets (AnTEC a n) n + -> Lets a n++threadTopLets config kenv tenv lts+ = case lts of+ LLet b x+ -> let (b', x') = threadTopBind config [] kenv tenv b x+ in LLet b' x'++ LRec bxs+ -> let tenv' = Env.extends (map fst bxs) tenv+ bxs' = [ threadTopBind config [ContextRec n] kenv tenv' b x + | (b, x) <- bxs+ , let BName n _ = b ]+ in LRec bxs'++ _ -> reannotate annotTail lts+++-- TopBind ------------------------------------------------------------------+-- | Thread state token into a top-level binding.+-- We assume every top-level binding is stateful function that needs to+-- accept and return the state token.+--+-- We inject the world type into the type of the function and then call+-- threadBind which will add the actual lambda for the new argument.+--+threadTopBind+ :: (Ord n, Show n, Pretty n)+ => Config a n+ -> [Context n]+ -> KindEnv n -> TypeEnv n+ -> Bind n -> Exp (AnTEC a n) n+ -> (Bind n, Exp a n)++threadTopBind config context kenv tenv b xBody+ = let tBind = typeOfBind b+ tBind' = injectStateType config tBind+ b' = replaceTypeOfBind tBind' b+ tenv' = Env.extend b' tenv+ tsArgs = fst $ takeTFunAllArgResult tBind'+ in ( b'+ , threadProc config context kenv tenv' xBody tsArgs)+++-- Arg ------------------------------------------------------------------------+-- | Thread state token into an argument expression.+-- If it is a syntactic function then we assume the function is stateful+-- and needs the state token added, otherwise return it unharmed.+threadArg + :: (Ord n, Show n, Pretty n)+ => Config a n+ -> [Context n]+ -> KindEnv n -> TypeEnv n+ -> Type n -> Exp (AnTEC a n) n+ -> Exp a n++threadArg config context kenv tenv t xx+ = case xx of+ XLam{} -> threadProcArg config context kenv tenv t xx+ XLAM{} -> threadProcArg config context kenv tenv t xx+ _ -> reannotate annotTail xx++threadProcArg config context kenv tenv t xx+ = let tsArgs = fst $ takeTFunAllArgResult t+ in threadProc config context kenv tenv xx tsArgs+++-- Proc -----------------------------------------------------------------------+-- | Thread world token into the body of a stateful function (procedure).+threadProc+ :: (Ord n, Show n, Pretty n)+ => Config a n+ -> [Context n]+ -> KindEnv n -> TypeEnv n+ -> Exp (AnTEC a n) n -- Whole expression, including lambdas.+ -> [Type n] -- Types of function parameters.+ -> Exp a n++-- We're out of parameters. +-- Now thread into the statements in the function body.+threadProc config context kenv tenv xx []+ = threadProcBody config context kenv tenv xx++-- We're still decending past all the lambdas.+-- When we get to the inner-most one then add the state parameter.+threadProc config context kenv tenv xx (t : tsArgs)+ = case xx of+ XLAM a b x+ -> let kenv' = Env.extend b kenv+ x' = threadProc config context kenv' tenv x tsArgs+ in XLAM (annotTail a) b x'++ XLam a b x + -> let tenv' = Env.extend b tenv+ x' = threadProc config context kenv tenv' x tsArgs+ in XLam (annotTail a) b x'++ -- Inject a new lambda to bind the state parameter.+ _ | a <- annotOfExp xx+ , t == configTokenType config + -> let b' = BAnon (configTokenType config)+ tenv' = Env.extend b' tenv+ x' = threadProc config context kenv tenv' xx tsArgs+ in XLam (annotTail a) b' x'++ -- We've decended past all the lambdas,+ -- so now thread into the procedure body.+ _ -> threadProcBody config context kenv tenv xx+++-- | Thread world token into the body of a procedure,+-- after we've decended past all the lambdas.+threadProcBody + :: (Ord n, Show n, Pretty n)+ => Config a n + -> [Context n]+ -> KindEnv n -> TypeEnv n+ -> Exp (AnTEC a n) n + -> Exp a n++threadProcBody config context kenv tenv xx+ = case xx of+ + -- Recursive let bindings in a procedure body.+ -- These will be local loops.+ XLet a (LRec bxs) x2+ -> let bxs' = [threadTopBind config + (context ++ [ContextRec n]) + kenv tenv b x+ | (b, x) <- bxs + , let BName n _ = b ]++ tenv' = Env.extends (map fst bxs) tenv+++ x2' = threadProcBody config + (context ++ [ContextFun n + | (b, _x) <- bxs+ , let BName n _ = b ])+ kenv tenv' x2+ in XLet (annotTail a) (LRec bxs') x2'++ -- A statement in the procedure body.+ XLet _ (LLet b x) x2+ | Just (XVar a u, xsArgs) <- takeXApps x+ , Just n <- takeNameOfBound u+ , Just tOld <- Env.lookup u tenv+ , Just tNew <- configThreadMe config n tOld+ , Just mkPat <- configThreadPat config n+ -> let + tWorld = configTokenType config++ -- Add world token as final argument + xsArgs' = xsArgs ++ [XVar a (UIx 0)]++ -- Thread into possibly higher order arguments.+ tsArgs = fst $ takeTFunAllArgResult tNew+ xsArgs'' = zipWith (threadArg config context kenv tenv) tsArgs xsArgs'++ -- Build the final expression.+ u' = replaceTypeOfBound tNew u+ x' = xApps (annotTail a) (XVar (annotTail a) u') xsArgs''++ -- Thread into let-expression body.+ tenv' = Env.extend b tenv+ x2' = threadProcBody config context kenv tenv' x2+ pat' = mkPat (BAnon tWorld) [b]+ in XCase (annotTail a) x' [AAlt pat' x2']+++ -- Let bound effectful function.+ -- Needs to be converted to a 'case'.+ XLet a (LLet b x1) x2+ | Just (XVar _ (UName n), _xsArgs) <- takeXApps x1+ , elem (ContextFun n) context+ , Just mkPat <- configThreadPat config n+ -> let + tWorld = configTokenType config+ a' = annotTail a+ x1' = XApp a' (reannotate annotTail x1) (XVar a' (UIx 0))+ x2' = threadProcBody config context kenv tenv x2+ pat' = mkPat (BAnon tWorld) [b]++ in XCase (annotTail a) x1' [AAlt pat' x2']+++ -- A pure binding that doesn't need the token.+ XLet a lts x+ -> let (bks, bts) = bindsOfLets lts+ kenv' = Env.extends bks kenv+ tenv' = Env.extends bts tenv+ lts' = reannotate annotTail lts+ x' = threadProcBody config context kenv' tenv' x+ in XLet (annotTail a) lts' x'+++ -- Case of an effectful function.+ XCase a xScrut [AAlt (PData _dc bs) xBody]+ | Just ((XVar _ (UName n), _xsArgs)) <- takeXApps xScrut+ , elem (ContextFun n) context+ , Just mkPat <- configThreadPat config n+ -> let + a' = annotTail a+ tWorld = configTokenType config+ xScrut' = XApp a' (reannotate annotTail xScrut) (XVar a' (UIx 0))+ pat' = mkPat (BAnon tWorld) bs+ alt' = threadAlt config context kenv tenv + (AAlt pat' xBody)++ in XCase (annotTail a) xScrut' [alt']+++ -- Pure case. + XCase a x alts+ -> let alts' = map (threadAlt config context kenv tenv) alts+ x' = reannotate annotTail x+ in XCase (annotTail a) x' alts'++ -- We shouldn't see these things in a proc body.+ XLAM{} -> error "ddc-core-simpl.Thread: unexpected XLAM"+ XLam{} -> error "ddc-core-simpl.Thread: unexpected XLam"+ XCast{} -> error "ddc-core-simpl.Thread: unexpected cast."++ XType a t + -> XType (annotTail a) t+ + XWitness a w + -> XWitness (annotTail a) (reannotate annotTail w)++ -- Tailcalls+ XApp a _ _+ | Just ((XVar _ (UName n), _xsArgs)) <- takeXApps xx+ , elem (ContextRec n) context+ -> let a' = annotTail a+ in XApp a' (reannotate annotTail xx)+ (XVar a' (UIx 0))+++ -- For XVar, XCon, XApp as result value of function.+ _+ -- Otherwise wrap the returned value with a tuple holding+ -- the world.+ | otherwise+ -> let a = annotOfExp xx+ a' = AnTEC (configTokenType config) + (tBot kEffect) + (tBot kClosure)+ (annotTail a)++ xWorld = XVar a' (UIx 0)+ wrap = configWrapResultExp config+ in wrap xWorld xx++++-- | Thread world token into a case alternative+threadAlt + :: (Ord n, Show n, Pretty n)+ => Config a n + -> [Context n]+ -> KindEnv n -> TypeEnv n+ -> Alt (AnTEC a n) n + -> Alt a n++threadAlt config context kenv tenv (AAlt pat xx)+ = case pat of+ PDefault+ -> AAlt pat (threadProcBody config context kenv tenv xx)++ PData _ bs+ -> let tenv' = Env.extends bs tenv+ in AAlt pat (threadProcBody config context kenv tenv' xx)+ ++-------------------------------------------------------------------------------+-- | Inject the state token into the type of an effectful function.+-- Eg, change ([a b : Data]. a -> b -> Int) +-- to ([a b : Data]. a -> b -> World -> (World, Int)+injectStateType :: Eq n => Config a n -> Type n -> Type n+injectStateType config tt+ = let down = injectStateType config+ in case tt of+ TForall b x + -> TForall b (down x)++ TApp{}+ | (tsArg@(_ : _), tResult) <- takeTFunArgResult tt+ -> let tsArg' = tsArg ++ [configTokenType config]+ tResult' = injectStateType config tResult+ in foldr tFun tResult' tsArg'++ _ | tt == configTokenType config -> tt+ | tt == configVoidType config -> configTokenType config+ | otherwise -> configWrapResultType config tt+
+ DDC/Core/Transform/TransformDownX.hs view
@@ -0,0 +1,139 @@++-- | General purpose tree walking boilerplate.+module DDC.Core.Transform.TransformDownX+ ( TransformDownMX(..)+ , transformDownX+ , transformDownX')+where+import DDC.Core.Module+import DDC.Core.Exp.Annot+import DDC.Type.Env (KindEnv, TypeEnv)+import Data.Functor.Identity+import Control.Monad+import qualified DDC.Type.Env as Env+++-- | Top-down rewrite of all core expressions in a thing.+transformDownX+ :: forall (c :: * -> * -> *) a n+ . (Ord n, TransformDownMX Identity c)+ => (KindEnv n -> TypeEnv n -> Exp a n -> Exp a n) + -- ^ The worker function is given the current+ -- kind and type environments.+ -> KindEnv n -- ^ Initial kind environment.+ -> TypeEnv n -- ^ Initial type environment.+ -> c a n -- ^ Transform this thing.+ -> c a n++transformDownX f kenv tenv xx+ = runIdentity + $ transformDownMX + (\kenv' tenv' x -> return (f kenv' tenv' x)) + kenv tenv xx+++-- | Like transformDownX, but without using environments.+transformDownX'+ :: forall (c :: * -> * -> *) a n+ . (Ord n, TransformDownMX Identity c)+ => (Exp a n -> Exp a n) + -- ^ The worker function is given the current+ -- kind and type environments.+ -> c a n -- ^ Transform this thing.+ -> c a n++transformDownX' f xx+ = transformDownX (\_ _ -> f) Env.empty Env.empty xx+++-------------------------------------------------------------------------------+class TransformDownMX m (c :: * -> * -> *) where+ -- | Top-down monadic rewrite of all core expressions in a thing.+ transformDownMX+ :: Ord n+ => (KindEnv n -> TypeEnv n -> Exp a n -> m (Exp a n))+ -- ^ The worker function is given the current+ -- kind and type environments.+ -> KindEnv n -- ^ Initial kind environment.+ -> TypeEnv n -- ^ Initial type environment.+ -> c a n -- ^ Transform this thing.+ -> m (c a n)+++instance Monad m => TransformDownMX m Module where+ transformDownMX f kenv tenv !mm+ = do x' <- transformDownMX f kenv tenv $ moduleBody mm+ return $ mm { moduleBody = x' }+++instance Monad m => TransformDownMX m Exp where+ transformDownMX f kenv tenv !xx+ = {-# SCC transformDownMX #-} + f kenv tenv xx >>= \xx' -> + case xx' of+ XVar{} -> return xx'+ XCon{} -> return xx'++ XLAM a b x1+ -> liftM3 XLAM (return a) (return b)+ (transformDownMX f (Env.extend b kenv) tenv x1)++ XLam a b x1 + -> liftM3 XLam (return a) (return b) + (transformDownMX f kenv (Env.extend b tenv) x1)++ XApp a x1 x2 + -> liftM3 XApp (return a) + (transformDownMX f kenv tenv x1) + (transformDownMX f kenv tenv x2)++ XLet a lts x+ -> do lts' <- transformDownMX f kenv tenv lts+ let kenv' = Env.extends (specBindsOfLets lts') kenv+ let tenv' = Env.extends (valwitBindsOfLets lts') tenv+ x' <- transformDownMX f kenv' tenv' x+ return $ XLet a lts' x'+ + XCase a x alts+ -> liftM3 XCase (return a)+ (transformDownMX f kenv tenv x)+ (mapM (transformDownMX f kenv tenv) alts)++ XCast a c x + -> liftM3 XCast+ (return a) (return c)+ (transformDownMX f kenv tenv x)++ XType{} -> return xx'+ XWitness{} -> return xx'+++instance Monad m => TransformDownMX m Lets where+ transformDownMX f kenv tenv xx+ = case xx of+ LLet b x+ -> liftM2 LLet (return b)+ (transformDownMX f kenv tenv x)+ + LRec bxs+ -> do let (bs, xs) = unzip bxs+ let tenv' = Env.extends bs tenv+ xs' <- mapM (transformDownMX f kenv tenv') xs+ return $ LRec $ zip bs xs'++ LPrivate{}+ -> return xx+++instance Monad m => TransformDownMX m Alt where+ transformDownMX f kenv tenv alt+ = case alt of+ AAlt p@(PData _ bsArg) x+ -> let tenv' = Env.extends bsArg tenv+ in liftM2 AAlt (return p) + (transformDownMX f kenv tenv' x)++ AAlt PDefault x+ -> liftM2 AAlt (return PDefault)+ (transformDownMX f kenv tenv x) +
+ DDC/Core/Transform/TransformModX.hs view
@@ -0,0 +1,45 @@++-- | Helper for transforming the bindings in a module+module DDC.Core.Transform.TransformModX+ ( transformModX+ , transformModLet+ )+where+import DDC.Core.Module+import DDC.Core.Exp.Annot++import Control.Arrow+++-- | Apply transform to each expression let binding in module+transformModX :: (Exp a n -> Exp a n)+ -> Module a n+ -> Module a n+transformModX f mm+ = transformModLet (const f) mm+++-- | Apply transform to each expression let binding in module, with bind too+transformModLet :: (Bind n -> Exp a n -> Exp a n)+ -> Module a n+ -> Module a n+transformModLet f mm+ = let body = moduleBody mm++ (lets,xx) = splitXLetsAnnot body+ lets' = map (first go) lets++ body' = xLetsAnnot lets' xx++ in mm { moduleBody = body' }+ where+ go (LRec binds)+ = LRec [ (b, f b x)+ | (b, x) <- binds]++ go (LLet b x)+ = LLet b (f b x)++ go l+ = l+
+ DDC/Core/Transform/TransformUpX.hs view
@@ -0,0 +1,133 @@+-- | General purpose tree walking boilerplate.+module DDC.Core.Transform.TransformUpX+ ( TransformUpMX(..)+ , transformUpX+ , transformUpX')+where+import DDC.Core.Module+import DDC.Core.Exp.Annot+import Data.Functor.Identity+import Control.Monad+import DDC.Core.Env.EnvX (EnvX)+import qualified DDC.Core.Env.EnvX as EnvX+++-- | Bottom up rewrite of all core expressions in a thing.+transformUpX+ :: forall (c :: * -> * -> *) a n+ . (Ord n, TransformUpMX Identity c)+ => (EnvX n -> Exp a n -> Exp a n) + -- ^ The worker function is given the current kind and type environments.+ -> EnvX n -- ^ Initial environment.+ -> c a n -- ^ Transform this thing.+ -> c a n++transformUpX f env xx+ = runIdentity + $ transformUpMX (\env' x -> return (f env' x)) env xx+++-- | Like transformUpX, but without using environments.+transformUpX'+ :: forall (c :: * -> * -> *) a n+ . (Ord n, TransformUpMX Identity c)+ => (Exp a n -> Exp a n) + -- ^ The worker function is given the current+ -- kind and type environments.+ -> c a n -- ^ Transform this thing.+ -> c a n++transformUpX' f xx+ = transformUpX (\_ -> f) EnvX.empty xx+++-------------------------------------------------------------------------------+class TransformUpMX m (c :: * -> * -> *) where+ -- | Bottom-up monadic rewrite of all core expressions in a thing.+ transformUpMX+ :: Ord n+ => (EnvX n -> Exp a n -> m (Exp a n))+ -- ^ The worker function is given the current+ -- kind and type environments.+ -> EnvX n -- ^ Initial environment.+ -> c a n -- ^ Transform this thing.+ -> m (c a n)+++instance Monad m => TransformUpMX m Module where+ transformUpMX f env !mm+ = do x' <- transformUpMX f env $ moduleBody mm+ return $ mm { moduleBody = x' }+++instance Monad m => TransformUpMX m Exp where+ transformUpMX f env !xx+ = (f env =<<)+ $ case xx of+ XVar{} -> return xx+ XCon{} -> return xx++ XLAM a b x1+ -> liftM3 XLAM (return a) (return b)+ (transformUpMX f (EnvX.extendT b env) x1)++ XLam a b x1 + -> liftM3 XLam (return a) (return b) + (transformUpMX f (EnvX.extendX b env) x1)++ XApp a x1 x2 + -> liftM3 XApp (return a) + (transformUpMX f env x1) + (transformUpMX f env x2)++ XLet a lts x+ -> do lts' <- transformUpMX f env lts++ let env' = EnvX.extendsX (valwitBindsOfLets lts')+ $ EnvX.extendsT (specBindsOfLets lts') + $ env++ x' <- transformUpMX f env' x+ return $ XLet a lts' x'+ + XCase a x alts+ -> liftM3 XCase (return a)+ (transformUpMX f env x)+ (mapM (transformUpMX f env) alts)++ XCast a c x + -> liftM3 XCast+ (return a) (return c)+ (transformUpMX f env x)++ XType{} -> return xx+ XWitness{} -> return xx+++instance Monad m => TransformUpMX m Lets where+ transformUpMX f env xx+ = case xx of+ LLet b x+ -> liftM2 LLet (return b)+ (transformUpMX f env x)+ + LRec bxs+ -> do let (bs, xs) = unzip bxs+ let env' = EnvX.extendsX bs env+ xs' <- mapM (transformUpMX f env') xs+ return $ LRec $ zip bs xs'++ LPrivate{} -> return xx+++instance Monad m => TransformUpMX m Alt where+ transformUpMX f env alt+ = case alt of+ AAlt p@(PData _ bsArg) x+ -> let env' = EnvX.extendsX bsArg env+ in liftM2 AAlt (return p) + (transformUpMX f env' x)++ AAlt PDefault x+ -> liftM2 AAlt (return PDefault)+ (transformUpMX f env x)
− DDC/Core/Transform/TransformX.hs
@@ -1,112 +0,0 @@---- | General purpose tree walking boilerplate.-module DDC.Core.Transform.TransformX- ( TransformUpMX(..)- , transformUpX)-where-import DDC.Core.Exp-import DDC.Core.Compounds-import DDC.Type.Env (Env)-import Data.Functor.Identity-import Control.Monad-import qualified DDC.Type.Env as Env----- | Bottom up rewrite of all core expressions in a thing.-transformUpX- :: forall (c :: * -> * -> *) a n- . (Ord n, TransformUpMX Identity c)- => (Env n -> Env n -> Exp a n -> Exp a n) - -- ^ The worker function is given the current kind and type environments.- -> Env n -- ^ Initial kind environment.- -> Env n -- ^ Initial type environment.- -> c a n -- ^ Transform this thing.- -> c a n--transformUpX f kenv tenv xx- = runIdentity - $ transformUpMX - (\kenv' tenv' x -> return (f kenv' tenv' x)) - kenv tenv xx---class TransformUpMX m (c :: * -> * -> *) where- -- | Bottom-up monadic rewrite of all core expressions in a thing.- transformUpMX- :: Ord n- => (Env n -> Env n -> Exp a n -> m (Exp a n))- -- ^ The worker function is given the current kind and type environments.- -> Env n -- ^ Initial kind environment.- -> Env n -- ^ Initial type environment.- -> c a n -- ^ Transform this thing.- -> m (c a n)--instance Monad m => TransformUpMX m Exp where- transformUpMX f kenv tenv xx- = (f kenv tenv =<<)- $ case xx of- XVar{} -> return xx- XCon{} -> return xx-- XLAM a b x1- -> liftM3 XLAM (return a) (return b)- (transformUpMX f (Env.extend b kenv) tenv x1)-- XLam a b x1 - -> liftM3 XLam (return a) (return b) - (transformUpMX f kenv (Env.extend b tenv) x1)-- XApp a x1 x2 - -> liftM3 XApp (return a) - (transformUpMX f kenv tenv x1) - (transformUpMX f kenv tenv x2)-- XLet a lts x- -> do lts' <- transformUpMX f kenv tenv lts- let kenv' = Env.extends (specBindsOfLets lts') kenv- let tenv' = Env.extends (valwitBindsOfLets lts') tenv- x' <- transformUpMX f kenv' tenv' x- return $ XLet a lts' x'- - XCase a x alts- -> liftM3 XCase (return a)- (transformUpMX f kenv tenv x)- (mapM (transformUpMX f kenv tenv) alts)-- XCast a c x - -> liftM3 XCast- (return a) (return c)- (transformUpMX f kenv tenv x)-- XType _ -> return xx- XWitness _ -> return xx---instance Monad m => TransformUpMX m Lets where- transformUpMX f kenv tenv xx- = case xx of- LLet m b x- -> liftM3 LLet (return m) (return b)- (transformUpMX f kenv tenv x)- - LRec bxs- -> do let (bs, xs) = unzip bxs- let tenv' = Env.extends bs tenv- xs' <- mapM (transformUpMX f kenv tenv') xs- return $ LRec $ zip bs xs'-- LLetRegion{} -> return xx- LWithRegion{} -> return xx---instance Monad m => TransformUpMX m Alt where- transformUpMX f kenv tenv alt- = case alt of- AAlt p@(PData _ bsArg) x- -> let tenv' = Env.extends bsArg tenv- in liftM2 AAlt (return p) - (transformUpMX f kenv tenv' x)-- AAlt PDefault x- -> liftM2 AAlt (return PDefault)- (transformUpMX f kenv tenv x)
@@ -0,0 +1,334 @@++module DDC.Core.Transform.Unshare+ (unshareModule)+where+import DDC.Core.Exp.Annot.AnTEC+import DDC.Core.Exp.Annot+import DDC.Core.Module+import DDC.Type.Transform.SubstituteT+import Data.Map (Map)+import qualified Data.Map.Strict as Map+++-------------------------------------------------------------------------------+-- | Apply the unsharing transform to a module.+unshareModule + :: (Show a, Ord n, Show n)+ => Module (AnTEC a n) n -> Module (AnTEC a n) n++unshareModule !mm+ = let+ -- Add extra parameters to the types of imported CAFs.+ importValuesNts + = [ let (iv', m) = addParamsImportValue iv+ in ((n, iv'), m)+ | (n, iv) <- moduleImportValues mm]++ (importValues', ntssImport')+ = unzip importValuesNts++ -- Add extra parameters to the CAFs,+ -- returning the names of the ones we've transformed+ -- along with the transformed module body.+ (ntsBody, xx) = addParamsX $ moduleBody mm++ -- Add the corresponding arguments to each use.+ nts' = Map.union (Map.unions ntssImport') ntsBody+ xx' = addArgsX nts' xx+ + -- Update the types of exports with the transformed ones.+ exportValues' + = [ (n, updateExportSource nts' ex)+ | (n, ex) <- moduleExportValues mm ]++ in mm { moduleBody = xx' + , moduleExportValues = exportValues' + , moduleImportValues = importValues' }+++-------------------------------------------------------------------------------+-- | If this import def imports a CAF then then add an extra parameter to its+-- type, assuming that the unsharing transform has also been applied to the+-- imported module.+--+addParamsImportValue + :: ImportValue n (Type n)+ -> (ImportValue n (Type n), Map n (Type n))++addParamsImportValue iv + = case iv of+ ImportValueModule m n t (Just (nType, nValue, nBoxes))+ -> case addParamsT t of+ Just t' + -> ( ImportValueModule m n t' + (Just (nType, nValue + 1, nBoxes))+ , Map.singleton n t')++ Nothing + -> ( iv, Map.empty)++ ImportValueModule{} -> (iv, Map.empty)+ ImportValueSea{} -> (iv, Map.empty)+++-- | If this is the type of a CAF then add an extra unit parameter to it.+addParamsT :: Type n -> Maybe (Type n)+addParamsT tt+ = case tt of+ TVar{} -> Just $ tUnit `tFun` tt+ TCon{} -> Just $ tUnit `tFun` tt++ TAbs b tBody+ -> do tBody' <- addParamsT tBody+ return $ TAbs b tBody'++ TApp{}+ -> case takeTFun tt of+ Nothing -> Just $ tUnit `tFun` tt+ Just _ -> Nothing++ TForall b tBody+ -> do tBody' <- addParamsT tBody+ return $ TForall b tBody'++ TSum{}+ -> Nothing+++-------------------------------------------------------------------------------+-- | Add unit parameters to the top-level CAFs in the given module body,+-- returning a map of names of transformed CAFs to their transformed +-- types.+addParamsX + :: Ord n+ => Exp (AnTEC a n) n -- Module body to transform.+ -> ( Map n (Type n) -- Map of transformed bindings to their+ -- transformed types.+ , Exp (AnTEC a n) n) -- Transformed module body.++addParamsX xx+ = case xx of+ -- Transform all the top-level bindings of a module body.+ XLet a (LRec bxs) xBody+ -> let (ns, bxs') = addParamsBXS a bxs+ in ( ns+ , XLet a (LRec bxs') xBody)++ _ -> ( Map.empty+ , xx)+++-- | Add unit parameters to the bound CAFs in the given list.+addParamsBXS _a []+ = (Map.empty, [])++addParamsBXS a ((b, x) : bxs)+ = let (ns1, b', x') = addParamsBX a b x+ (ns2, bxs') = addParamsBXS a bxs+ in ( Map.union ns1 ns2+ , (b', x') : bxs')+++-- | Add unit parameter to a single top-level binding, if it needs one.+addParamsBX _ b@(BName n _) x+ = case addParamsBodyX x of+ Nothing + -> (Map.empty, b, x)++ Just (x', t')+ -> ( Map.singleton n t'+ , replaceTypeOfBind t' b+ , x')++addParamsBX _ b x+ = (Map.empty, b, x)+++-- | Add unit parameters to the right of a let-binding.+addParamsBodyX xx+ = case xx of+ -- This binding already has an outer value abstraction,+ -- so we don't need to add any more.+ XLam{} + -> Nothing++ -- Decend under type abstractions. To keep the supers+ -- in standard form with all the type abstractions first, + -- if we need to add a value abstraction we want to add it+ -- under exising type abstractions.+ XLAM a bParam xBody+ -> case addParamsBodyX xBody of+ Nothing + -> Nothing++ Just (xBody', tBody')+ -> let t' = TForall bParam tBody'+ a' = a { annotType = t' }+ in Just ( XLAM a' bParam xBody', t')++ -- We've hit a plain value, so need to wrap it in a + -- value abstraction.+ _ + -> let a = annotOfExp xx+ t' = tFun tUnit (annotType a)+ a' = a { annotType = t' }+ in Just (XLam a' (BNone tUnit) xx, t')+++-------------------------------------------------------------------------------+-- | Decend into an expression looking for applications of CAFs that +-- we've already added an extra unit parameter to. When we find them,+-- add the matching unit argument.+--+addArgsX :: (Show n, Ord n, Show a)+ => Map n (Type n) -- ^ Map of names of CAFs that we've added + -- parameters to, to their transformed types.+ -> Exp (AnTEC a n) n -- ^ Transform this expression.+ -> Exp (AnTEC a n) n -- ^ Transformed expression.++addArgsX nts xx+ = let downX = addArgsX nts+ downLts = addArgsLts nts+ downA = addArgsAlt nts++ in case xx of++ -- Add an extra argument for a monomorphic CAF.+ XVar a (UName n)+ -> case Map.lookup n nts of+ Just tF + -> let xx' = XVar (a { annotType = tF }) (UName n)+ in wrapAppX a tF xx'++ Nothing -> xx++ XVar{} -> xx+ XCon{} -> xx++ XApp{} -> addArgsAppX nts xx []++ -- For the rest of the constructs their types do not+ -- change during the transform so we can resuse the old ones.+ XLAM a b xBody -> XLAM a b (downX xBody)+ XLam a b xBody -> XLam a b (downX xBody)+ XLet a lts xBody -> XLet a (downLts lts) (downX xBody)+ XCase a xScrut as -> XCase a (downX xScrut) (map downA as)+ XCast a c x -> XCast a c (downX x)+ XType{} -> xx+ XWitness{} -> xx+++addArgsAppX !nts !xx !ats+ = let downX = addArgsX nts+ tA = annotType $ annotOfExp xx+ in case xx of+ XVar a (UName n)+ -> case Map.lookup n nts of+ Just tF + -> let xx' = XVar (a { annotType = tF }) (UName n)+ (x1, t1) = wrapAtsX xx' tF ats+ x2 = wrapAppX a t1 x1+ in x2++ Nothing + -> fst $ wrapAtsX xx tA ats++ XVar{} + -> fst $ wrapAtsX xx tA ats++ XCon{} + -> fst $ wrapAtsX xx tA ats++ XApp _a1 x1 (XType a2 t)+ -> addArgsAppX nts x1 ((a2, t) : ats)++ XApp a x1 x2+ -> XApp a (addArgsAppX nts x1 ats) (downX x2)++ _ -> fst $ wrapAtsX xx tA ats+++addArgsLts nts lts+ = let downX = addArgsX nts+ in case lts of+ LLet b x -> LLet b (downX x)+ LRec bxs -> LRec [(b, downX x) | (b, x) <- bxs]+ LPrivate{} -> lts+++addArgsAlt nts aa + = let downX = addArgsX nts+ in case aa of+ AAlt p x -> AAlt p (downX x)+++-- Wrap an expression with an application of a unit value.+wrapAppX :: (Show a, Show n)+ => AnTEC a n+ -> Type n+ -> Exp (AnTEC a n) n + -> Exp (AnTEC a n) n++wrapAppX a tF xF+ | Just (_, tResult) <- takeTFun tF+ = let a' = annotOfExp xF+ aR = a' { annotType = tResult }+ aV = a' { annotType = tF }+ aU = a' { annotType = tUnit }+ xF' = mapAnnotOfExp (const aV) xF+ in XApp aR xF' (xUnit aU)+++ -- ISSUE #384: Unshare transform produces AST node with wrong type annotation.+ | Just (bs, tBody) <- takeTForalls tF+ = let Just us = sequence + $ map takeSubstBoundOfBind bs++ xF' = makeXLamFlags a [(True, b) | b <- bs] + $ wrapAppX a tBody+ $ xApps a xF (map (XType a) $ map TVar us)++ in xF'+++ | otherwise+ = xF+++-- Apply the given type arguments to an expression.+wrapAtsX !xF !tF []+ = (xF, tF)++wrapAtsX !xF !tF ((aArg, tArg): ats)+ = case tF of + TForall bParam tBody+ -> let a = annotOfExp xF+ tR = substituteT bParam tArg tBody+ aR = a { annotType = tR }+ aV = a { annotType = tF }+ xF' = mapAnnotOfExp (const aV) xF+ in wrapAtsX+ (XApp aR xF' (XType aArg tArg))+ tR ats++ _ -> (xF, tF)+++-------------------------------------------------------------------------------+-- | Update the types of exported things with the ones in +-- the give map.+updateExportSource + :: Ord n+ => Map n (Type n) + -> ExportSource n (Type n) -> ExportSource n (Type n)++updateExportSource mm ex+ = case ex of+ ExportSourceLocal n _t+ -> case Map.lookup n mm of+ Nothing -> ex+ Just t' -> ExportSourceLocal n t'++ ExportSourceLocalNoType _+ -> ex+
+ DDC/Type/Transform/Alpha.hs view
@@ -0,0 +1,55 @@++module DDC.Type.Transform.Alpha+ (Alpha(..))+where+import DDC.Type.Exp+import DDC.Type.Sum+++class Alpha (c :: * -> *) where+ -- | Apply a function to all the names in a thing.+ alpha :: forall n1 n2. Ord n2 => (n1 -> n2) -> c n1 -> c n2+ ++instance Alpha Type where+ alpha f tt+ = case tt of+ TVar u -> TVar (alpha f u)+ TCon c -> TCon (alpha f c)+ TAbs b t -> TAbs (alpha f b) (alpha f t)+ TApp t1 t2 -> TApp (alpha f t1) (alpha f t2)+ TForall b t -> TForall (alpha f b) (alpha f t)+ TSum ts -> TSum (alpha f ts)+++instance Alpha TypeSum where+ alpha f ts+ = fromList (alpha f $ kindOfSum ts) $ map (alpha f) $ toList ts+++instance Alpha Bind where+ alpha f bb+ = case bb of+ BName n t -> BName (f n) (alpha f t)+ BAnon t -> BAnon (alpha f t)+ BNone t -> BNone (alpha f t)+ ++instance Alpha Bound where+ alpha f uu+ = case uu of+ UIx i -> UIx i+ UName n -> UName (f n)+ UPrim n t -> UPrim (f n) (alpha f t)+++instance Alpha TyCon where+ alpha f cc+ = case cc of+ TyConSort sc -> TyConSort sc+ TyConKind kc -> TyConKind kc+ TyConWitness tc -> TyConWitness tc+ TyConSpec tc -> TyConSpec tc+ TyConBound u k -> TyConBound (alpha f u) (alpha f k)+ TyConExists i k -> TyConExists i (alpha f k)+
DDC/Type/Transform/AnonymizeT.hs view
@@ -4,8 +4,7 @@ , AnonymizeT(..) , pushAnonymizeBindT) where-import DDC.Type.Exp-import DDC.Type.Compounds+import DDC.Type.Exp.Simple import Data.List import qualified DDC.Type.Sum as T @@ -36,13 +35,17 @@ TCon{} -> tt - TForall b t + TAbs b t -> let (kstack', b') = pushAnonymizeBindT kstack b- in TForall b' (anonymizeWithT kstack' t)+ in TAbs b' (anonymizeWithT kstack' t) TApp t1 t2 -> TApp (anonymizeWithT kstack t1) (anonymizeWithT kstack t2) + TForall b t + -> let (kstack', b') = pushAnonymizeBindT kstack b+ in TForall b' (anonymizeWithT kstack' t)+ TSum ss -> TSum (anonymizeWithT kstack ss) @@ -57,17 +60,21 @@ instance AnonymizeT Bound where anonymizeWithT kstack bb = case bb of- UName _ t+ UName _ | Just ix <- findIndex (boundMatchesBind bb) kstack- -> UIx ix (anonymizeWithT kstack t)+ -> UIx ix _ -> bb -- Push ------------------------------------------------------------------------- Push a binding occurrence of a type variable on the stack, --- returning the anonyized binding occurrence and the new stack.-pushAnonymizeBindT :: Ord n => [Bind n] -> Bind n -> ([Bind n], Bind n)+-- | Push a binding occurrence of a level-1 variable on the stack, +-- returning the anonyized binding occurrence and the new stack.+pushAnonymizeBindT + :: [Bind n] -- ^ Stack for Spec binders (level-1)+ -> Bind n + -> ([Bind n], Bind n)+ pushAnonymizeBindT kstack b = let t' = typeOfBind b kstack' = b : kstack
− DDC/Type/Transform/Rename.hs
@@ -1,53 +0,0 @@--module DDC.Type.Transform.Rename- (Rename(..))-where-import DDC.Type.Exp-import DDC.Type.Sum---class Rename (c :: * -> *) where- -- | Apply a function to all the names in a thing.- rename :: forall n1 n2. Ord n2 => (n1 -> n2) -> c n1 -> c n2- --instance Rename Type where- rename f tt- = case tt of- TVar u -> TVar (rename f u)- TCon c -> TCon (rename f c)- TForall b t -> TForall (rename f b) (rename f t)- TApp t1 t2 -> TApp (rename f t1) (rename f t2)- TSum ts -> TSum (rename f ts)---instance Rename TypeSum where- rename f ts- = fromList (rename f $ kindOfSum ts) $ map (rename f) $ toList ts---instance Rename Bind where- rename f bb- = case bb of- BName n t -> BName (f n) (rename f t)- BAnon t -> BAnon (rename f t)- BNone t -> BNone (rename f t)- --instance Rename Bound where- rename f uu- = case uu of- UIx i k -> UIx i (rename f k)- UName n k -> UName (f n) (rename f k)- UPrim n k -> UName (f n) (rename f k)---instance Rename TyCon where- rename f cc- = case cc of- TyConSort sc -> TyConSort sc- TyConKind kc -> TyConKind kc- TyConWitness tc -> TyConWitness tc- TyConSpec tc -> TyConSpec tc- TyConBound u -> TyConBound $ rename f u-
LICENSE view
@@ -1,7 +1,7 @@ -------------------------------------------------------------------------------- The Disciplined Disciple Compiler License (MIT style) -Copyrite (K) 2007-2012 The Disciplined Disciple Compiler Strike Force+Copyrite (K) 2007-2016 The Disciplined Disciple Compiler Strike Force All rights reversed. Permission is hereby granted, free of charge, to any person obtaining a copy@@ -13,18 +13,4 @@ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.----------------------------------------------------------------------------------Under Australian law copyright is free and automatic.-By contributing to DDC authors grant all rights they have regarding their-contributions to the other members of the Disciplined Disciple Compiler Strike-Force, past, present and future, as well as placing their contributions under-the above license.--Use "darcs show authors" to get a list of Strike Force members.- ---------------------------------------------------------------------------------Redistributions of libraries in ./external are governed by their own licenses:-- - TinyPTC GNU Lesser General Public License-
ddc-core-simpl.cabal view
@@ -1,5 +1,5 @@ Name: ddc-core-simpl-Version: 0.2.1.2+Version: 0.4.3.1 License: MIT License-file: LICENSE Author: The Disciplined Disciple Compiler Strike Force@@ -9,39 +9,87 @@ Stability: experimental Category: Compilers/Interpreters Homepage: http://disciple.ouroborus.net-Bug-reports: disciple@ouroborus.net-Synopsis: Disciple Core language simplifying code transformations.-Description: Disciple Core language simplifying code transformations.+Synopsis: Disciplined Disciple Compiler code transformations.+Description: Disciplined Disciple Compiler code transformations. Library Build-Depends: - base == 4.6.*,+ base >= 4.6 && < 4.10,+ array >= 0.4 && < 0.6,+ deepseq >= 1.3 && < 1.5, containers == 0.5.*,- array >= 0.3 && < 0.5,- transformers == 0.3.*,- mtl == 2.1.*,- ddc-base == 0.2.1.*,- ddc-core == 0.2.1.*+ transformers == 0.5.*,+ mtl == 2.2.1.*,+ ddc-core == 0.4.3.* Exposed-modules:+ DDC.Core.Analysis.Arity+ DDC.Core.Analysis.Usage++ DDC.Core.Simplifier.Parser+ DDC.Core.Simplifier.Recipe+ DDC.Core.Simplifier.Result++ DDC.Core.Transform.Rewrite.Disjoint+ DDC.Core.Transform.Rewrite.Env+ DDC.Core.Transform.Rewrite.Match+ DDC.Core.Transform.Rewrite.Parser+ DDC.Core.Transform.Rewrite.Rule+ DDC.Core.Transform.AnonymizeX- DDC.Core.Transform.ANormal DDC.Core.Transform.Beta- DDC.Core.Transform.TransformX+ DDC.Core.Transform.Boxing+ DDC.Core.Transform.Bubble+ DDC.Core.Transform.Elaborate+ DDC.Core.Transform.Eta+ DDC.Core.Transform.Flatten+ DDC.Core.Transform.FoldCase+ DDC.Core.Transform.Forward+ DDC.Core.Transform.Inline+ DDC.Core.Transform.Lambdas+ DDC.Core.Transform.Namify+ DDC.Core.Transform.Prune+ DDC.Core.Transform.Rewrite+ DDC.Core.Transform.Snip+ DDC.Core.Transform.Thread+ DDC.Core.Transform.TransformDownX+ DDC.Core.Transform.TransformModX+ DDC.Core.Transform.TransformUpX+ DDC.Core.Transform.Unshare++ DDC.Core.Simplifier++ DDC.Type.Transform.Alpha DDC.Type.Transform.AnonymizeT- DDC.Type.Transform.Rename- ++ Other-modules:+ DDC.Core.Simplifier.Apply+ DDC.Core.Simplifier.Base+ DDC.Core.Simplifier.Lexer++ DDC.Core.Transform.Inline.Templates+ DDC.Core.Transform.Lambdas.Base+ DDC.Core.Transform.Lambdas.Lift+ DDC.Core.Transform.Rewrite.Error+ GHC-options: -Wall -fno-warn-orphans -fno-warn-missing-signatures+ -fno-warn-missing-methods -fno-warn-unused-do-bind Extensions: NoMonomorphismRestriction+ ExistentialQuantification+ MultiParamTypeClasses+ ScopedTypeVariables+ DeriveDataTypeable+ FlexibleInstances+ FlexibleContexts+ ParallelListComp ExplicitForAll KindSignatures PatternGuards- MultiParamTypeClasses- FlexibleContexts- FlexibleInstances+ BangPatterns+ RankNTypes