clash-lib-0.2: src/CLaSH/Normalize.hs
{-# LANGUAGE TemplateHaskell #-}
-- | Turn CoreHW terms into normalized CoreHW Terms
module CLaSH.Normalize where
import Control.Concurrent.Supply (Supply)
import Control.Lens ((.=))
import qualified Control.Lens as Lens
import qualified Control.Monad.State as State
import Data.HashMap.Lazy (HashMap)
import qualified Data.HashMap.Lazy as HashMap
import qualified Data.Map as Map
import qualified Data.Set as Set
import CLaSH.Core.FreeVars (termFreeIds)
import CLaSH.Core.Pretty (showDoc)
import CLaSH.Core.Term (Term, TmName)
import CLaSH.Core.Type (Type)
import CLaSH.Netlist.Types (HWType)
import CLaSH.Normalize.Strategy
import CLaSH.Normalize.Types
import CLaSH.Normalize.Util
import CLaSH.Rewrite.Types (DebugLevel (..), RewriteState (..),
bindings, dbgLevel)
import CLaSH.Rewrite.Util (liftRS, runRewrite,
runRewriteSession)
import CLaSH.Util
-- | Run a NormalizeSession in a given environment
runNormalization :: DebugLevel
-- ^ Level of debug messages to print
-> Supply
-- ^ UniqueSupply
-> HashMap TmName (Type,Term)
-- ^ Global Binders
-> (Type -> Maybe (Either String HWType))
-- ^ Hardcoded Type -> HWType translator
-> NormalizeSession a
-- ^ NormalizeSession to run
-> a
runNormalization lvl supply globals typeTrans
= flip State.evalState normState
. runRewriteSession lvl rwState
where
rwState = RewriteState 0 globals supply typeTrans
normState = NormalizeState
HashMap.empty
Map.empty
HashMap.empty
[]
(error "Report as bug: no curFun")
-- | Normalize a list of global binders
normalize :: [TmName]
-> NormalizeSession [(TmName,(Type,Term))]
normalize (bndr:bndrs) = do
let bndrS = showDoc bndr
exprM <- fmap (HashMap.lookup bndr) $ Lens.use bindings
case exprM of
Just (ty,expr) -> do
liftRS $ curFun .= bndr
normalizedExpr <- makeCachedT3' bndr normalized $
rewriteExpr ("normalization",normalization) (bndrS,expr)
let usedBndrs = Set.toList $ termFreeIds normalizedExpr
if bndr `elem` usedBndrs
then error $ $(curLoc) ++ "Expr belonging to bndr: " ++ bndrS ++ " remains recursive after normalization."
else do
prevNorm <- fmap HashMap.keys $ liftRS $ Lens.use normalized
let toNormalize = filter (`notElem` prevNorm) usedBndrs
normalizedOthers <- normalize (toNormalize ++ bndrs)
return ((bndr,(ty,normalizedExpr)):normalizedOthers)
Nothing -> error $ $(curLoc) ++ "Expr belonging to bndr: " ++ bndrS ++ " not found"
normalize [] = return []
-- | Rewrite a term according to the provided transformation
rewriteExpr :: (String,NormRewrite) -- ^ Transformation to apply
-> (String,Term) -- ^ Term to transform
-> NormalizeSession Term
rewriteExpr (nrwS,nrw) (bndrS,expr) = do
lvl <- Lens.view dbgLevel
let before = showDoc expr
let expr' = traceIf (lvl >= DebugFinal)
(bndrS ++ " before " ++ nrwS ++ ":\n\n" ++ before ++ "\n")
expr
rewritten <- runRewrite nrwS nrw expr'
let after = showDoc rewritten
traceIf (lvl >= DebugFinal)
(bndrS ++ " after " ++ nrwS ++ ":\n\n" ++ after ++ "\n") $
return rewritten
-- | Perform general \"clean up\" of the normalized (non-recursive) function
-- hierarchy. This includes:
--
-- * Inlining functions that simply \"wrap\" another function
cleanupGraph :: [TmName]
-- ^ Names of the functions to clean up
-> [(TmName,(Type,Term))]
-- ^ Global binders
-> NormalizeSession [(TmName,(Type,Term))]
cleanupGraph bndrs norm = do
bindings .= HashMap.fromList norm
cleanupGraph' ("cleanup",cleanup) bndrs
where
cleanupGraph' :: (String,NormRewrite) -> [TmName] -> NormalizeSession [(TmName,(Type,Term))]
cleanupGraph' rw (bndr:bndrs') = do
let bndrS = showDoc bndr
exprM <- fmap (HashMap.lookup bndr) $ Lens.use bindings
case exprM of
Just (ty,expr) -> do
liftRS $ curFun .= bndr
cleaned <- rewriteExpr rw (bndrS,expr)
let usedBndrs = Set.toList $ termFreeIds cleaned
cleanedOthers <- cleanupGraph' rw (usedBndrs ++ bndrs')
return $! (bndr,(ty,cleaned)):cleanedOthers
Nothing -> error $ $(curLoc) ++ "Expr belonging to bndr: " ++ bndrS ++ " not found"
cleanupGraph' _ [] = return []
-- | Check if the call graph (second argument), starting at the @topEnity@
-- (first argument) is non-recursive. Returns the list of normalized terms if
-- call graph is indeed non-recursive, errors otherwise.
checkNonRecursive :: TmName -- ^ @topEntity@
-> [(TmName,(Type,Term))] -- ^ List of normalized binders
-> [(TmName,(Type,Term))]
checkNonRecursive topEntity norm =
let cg = callGraph [] (HashMap.fromList $ map (second snd) norm) topEntity
in case recursiveComponents cg of
[] -> norm
rcs -> error $ "Callgraph after normalisation contains following recursive cycles: " ++ show rcs