netlist-0.2: Language/Netlist/Inline.hs
--------------------------------------------------------------------------------
-- |
-- Module : Language.Netlist.Inline
-- Copyright : (c) Signali Corp. 2010
-- License : All rights reserved
--
-- Maintainer : pweaver@signalicorp.com
-- Stability : experimental
-- Portability : non-portable
--
-- A simple inliner for a Netlist AST ('Language.Netlist.AST').
--------------------------------------------------------------------------------
{-# LANGUAGE Rank2Types, PatternGuards #-}
module Language.Netlist.Inline ( inlineModule ) where
import Data.Generics
--import Data.List
import Data.Maybe
import Data.Map (Map)
import qualified Data.Map as Map
import Language.Netlist.AST
-- -----------------------------------------------------------------------------
-- | Produce a new module in which some variables have been inlined. An
-- expression is inlined (and it\'s declaration removed) if it only used in one
-- place in the entire module.
inlineModule :: Module -> Module
inlineModule (Module name inputs outputs statics decls)
= Module name inputs outputs statics decls''
where
deps = getIdentExprs decls
bs = getBindings decls
bs' = Map.filterWithKey (shouldInline (map fst outputs) deps) bs
decls' = replaceExprs bs' decls
decls'' = removeDecls (Map.keys bs') decls'
-- given a list of identifier-to-expression bindings, replace the identifiers
-- everywhere in an AST. Note: "everywhere" applies bottom-up. We want
-- everywhere', which is top-down.
replaceExprs :: forall a. (Data a) => Map Ident Expr -> a -> a
replaceExprs bs a = everywhere' (mkT f) a
where
f e
| ExprVar x <- e, Just e' <- Map.lookup x bs
= e' -- replaceExprs bs e'
| otherwise = e
-- this is essentially a DCE pass. it removes the declarations that have been inlined.
removeDecls :: [Ident] -> [Decl] -> [Decl]
removeDecls xs = mapMaybe f
where
f d@(NetDecl x _ _)
= if elem x xs then Nothing else Just d
f d@(NetAssign x _)
= if elem x xs then Nothing else Just d
f decl
= Just decl
-- -----------------------------------------------------------------------------
-- utility functions
getBindings :: [Decl] -> Map Ident Expr
getBindings = Map.unions . map getDeclBinding
getDeclBinding :: Decl -> Map Ident Expr
getDeclBinding (NetDecl x _ (Just expr))
= Map.singleton x expr
getDeclBinding (NetAssign x expr)
= Map.singleton x expr
getDeclBinding _
= Map.empty
shouldInline :: [Ident] -> Map Ident [Expr] -> Ident -> Expr -> Bool
shouldInline ignore deps x e
| x `notElem` ignore, Just n <- checkUsers
= case e of
-- always inline trivial expressions regardless of the number of users.
ExprLit _ _ -> True
ExprString _ -> True
ExprVar _ -> True
ExprIndex _ _ -> True
ExprSlice _ _ _ -> True
-- ExprSliceOff _ _ _ -> True
-- never inline case expressions. as far as we know, there's no case
-- /expression/ in Verilog. we leave ExprCase alone here so that it may
-- be easier to translate to, for example, a case /statement/ in a
-- combinational process in HDL.
ExprCase {} -> False
-- any complex expressions should only be inlined if they're used once.
_ -> n == 1
| otherwise
= False
where
-- returns Nothing if this identifier cannot be inlined because it is
-- referred to by a Index/Project/FuncCall. returns Just n if the only
-- users are 'n' number of ExprVar expressions.
checkUsers
= if all checkUser zs then Just (length zs) else Nothing
where
zs = fromMaybe [] (Map.lookup x deps)
checkUser (ExprVar _) = True
checkUser _ = False
-- map each identifier to every expression that directly refers to that identifier.
getIdentExprs :: forall a. (Data a) => a -> Map Ident [Expr]
getIdentExprs a = f Map.empty (getAll a)
where
f :: Map Ident [Expr] -> [Expr] -> Map Ident [Expr]
f m [] = m
f m (expr:rest)
= f m' rest
where m' = case maybeExprIdent expr of
Just v -> Map.insertWith (++) v [expr] m
Nothing -> m
-- generically get a list of all terms of a certain type.
getAll :: forall a b. (Data a, Typeable b) => a -> [b]
getAll = listify (\_ -> True)
-- if an expression references an identifier directly, return the identifier.
-- note that subexpressions are not counted here!
maybeExprIdent :: Expr -> Maybe Ident
maybeExprIdent (ExprVar x) = Just x
maybeExprIdent (ExprIndex x _) = Just x
maybeExprIdent (ExprSlice x _ _) = Just x
maybeExprIdent (ExprSliceOff x _ _) = Just x
maybeExprIdent (ExprFunCall x _) = Just x
maybeExprIdent _ = Nothing
-- -----------------------------------------------------------------------------