purescript-0.6.3: src/Language/PureScript/DeadCodeElimination.hs
-----------------------------------------------------------------------------
--
-- Module : Language.PureScript.DeadCodeElimination
-- Copyright : (c) 2014 Phil Freeman
-- License : MIT
--
-- Maintainer : Phil Freeman <paf31@cantab.net>
-- Stability : experimental
-- Portability :
--
-- | Dead code elimination.
--
-----------------------------------------------------------------------------
module Language.PureScript.DeadCodeElimination (
eliminateDeadCode
) where
import Data.Graph
import Data.List
import Data.Maybe (mapMaybe)
import Language.PureScript.CoreFn
import Language.PureScript.Names
-- |
-- Eliminate all declarations which are not a transitive dependency of the entry point module
--
eliminateDeadCode :: [ModuleName] -> [Module a] -> [Module a]
eliminateDeadCode entryPoints ms = map go ms
where
go (Module mn imps exps foreigns ds) = Module mn imps exps' foreigns' ds'
where
ds' = filter (isUsed mn graph vertexFor entryPointVertices) ds
foreigns' = filter (isUsed' mn graph vertexFor entryPointVertices . foreignIdent) foreigns
names = concatMap bindIdents ds' ++ map foreignIdent foreigns'
exps' = filter (`elem` names) exps
declarations = concatMap declarationsByModule ms
(graph, _, vertexFor) = graphFromEdges $ map (\(key, deps) -> (key, key, deps)) declarations
entryPointVertices = mapMaybe (vertexFor . fst) . filter (\((mn, _), _) -> mn `elem` entryPoints) $ declarations
-- |
-- Extract declaration names for a binding group.
--
bindIdents :: Bind a -> [Ident]
bindIdents (NonRec name _) = [name]
bindIdents (Rec names) = map fst names
-- |
-- Extract the ident for a foreign declaration.
--
foreignIdent :: ForeignDecl -> Ident
foreignIdent (name, _, _) = name
-- |
-- Key type to use in graph
--
type Key = (ModuleName, Ident)
-- |
-- Find dependencies for each member in a module.
--
declarationsByModule :: Module a -> [(Key, [Key])]
declarationsByModule (Module mn _ _ fs ds) =
let fs' = map ((\name -> ((mn, name), [])) . foreignIdent) fs
in fs' ++ concatMap go ds
where
go :: Bind a -> [(Key, [Key])]
go d@(NonRec name _) = [((mn, name), dependencies d)]
go d@(Rec names') = map (\(name, _) -> ((mn, name), dependencies d)) names'
-- |
-- Find all referenced values within a binding group.
--
dependencies :: Bind a -> [Key]
dependencies =
let (f, _, _, _) = everythingOnValues (++) (const []) values binders (const [])
in nub . f
where
values :: Expr a -> [Key]
values (Var _ (Qualified (Just mn) ident)) = [(mn, ident)]
values _ = []
binders :: Binder a -> [Key]
binders (ConstructorBinder _ _ (Qualified (Just mn) ident) _) = [(mn, Ident $ runProperName ident)]
binders _ = []
-- |
-- Check whether a binding group is used.
--
isUsed :: ModuleName -> Graph -> (Key -> Maybe Vertex) -> [Vertex] -> Bind a -> Bool
isUsed mn graph vertexFor entryPointVertices (NonRec name _) =
isUsed' mn graph vertexFor entryPointVertices name
isUsed mn graph vertexFor entryPointVertices (Rec ds) =
any (isUsed' mn graph vertexFor entryPointVertices . fst) ds
-- |
-- Check whether a named declaration is used.
--
isUsed' :: ModuleName -> Graph -> (Key -> Maybe Vertex) -> [Vertex] -> Ident -> Bool
isUsed' mn graph vertexFor entryPointVertices name =
let Just v' = vertexFor (mn, name)
in any (\v -> path graph v v') entryPointVertices