module CLasH.Normalize.NormalizeTools where
import qualified Data.Monoid as Monoid
import qualified Data.Either as Either
import qualified Control.Monad as Monad
import qualified Control.Monad.Trans.Writer as Writer
import qualified Control.Monad.Trans.Class as Trans
import qualified Data.Accessor.Monad.Trans.State as MonadState
import CoreSyn
import qualified Name
import qualified Id
import qualified CoreSubst
import qualified Type
import qualified CoreUtils
import Outputable ( showSDoc, ppr, nest )
import CLasH.Normalize.NormalizeTypes
import CLasH.Translator.TranslatorTypes
import CLasH.VHDL.Constants (builtinIds)
import CLasH.Utils
import qualified CLasH.Utils.Core.CoreTools as CoreTools
import qualified CLasH.VHDL.VHDLTools as VHDLTools
everywhere :: Transform -> Transform
everywhere trans = applyboth (subeverywhere (everywhere trans)) trans
data NormDbgLevel =
NormDbgNone
| NormDbgFinal
| NormDbgApplied
| NormDbgAll
deriving (Eq, Ord)
normalize_debug = NormDbgFinal
apply :: (String, Transform) -> Transform
apply (name, trans) ctx expr = do
(expr', any_changed) <- Writer.listen $ trans ctx expr
let changed = Monoid.getAny any_changed
Monad.when changed $ Trans.lift (MonadState.modify tsTransformCounter (+1))
let before = showSDoc (nest 4 $ ppr expr) ++ "\nType: \n" ++ (showSDoc $ nest 4 $ ppr $ CoreUtils.exprType expr) ++ "\n"
let context = "Context: " ++ show ctx ++ "\n"
let after = showSDoc (nest 4 $ ppr expr') ++ "\nType: \n" ++ (showSDoc $ nest 4 $ ppr $ CoreUtils.exprType expr') ++ "\n"
traceIf (normalize_debug >= NormDbgApplied && changed) ("Changes when applying transform " ++ name ++ " to:\n" ++ before ++ context ++ "Result:\n" ++ after) $
traceIf (normalize_debug >= NormDbgAll && not changed) ("No changes when applying transform " ++ name ++ " to:\n" ++ before ++ context) $
return expr'
applyboth :: Transform -> Transform -> Transform
applyboth first second context expr = do
expr' <- first context expr
(expr'', changed) <- Writer.listen $ second context expr'
if Monoid.getAny $ changed
then
applyboth first second context expr''
else
return expr''
subeverywhere :: Transform -> Transform
subeverywhere trans c (App a b) = do
a' <- trans (AppFirst:c) a
b' <- trans (AppSecond:c) b
return $ App a' b'
subeverywhere trans c (Let (NonRec b bexpr) expr) = do
bexpr' <- trans (LetBinding:c) bexpr
expr' <- trans (LetBody:c) expr
return $ Let (NonRec b bexpr') expr'
subeverywhere trans c (Let (Rec binds) expr) = do
expr' <- trans (LetBody:c) expr
binds' <- mapM transbind binds
return $ Let (Rec binds') expr'
where
transbind :: (CoreBndr, CoreExpr) -> TransformMonad (CoreBndr, CoreExpr)
transbind (b, e) = do
e' <- trans (LetBinding:c) e
return (b, e')
subeverywhere trans c (Lam x expr) = do
expr' <- trans (LambdaBody:c) expr
return $ Lam x expr'
subeverywhere trans c (Case scrut b t alts) = do
scrut' <- trans (Other:c) scrut
alts' <- mapM transalt alts
return $ Case scrut' b t alts'
where
transalt :: CoreAlt -> TransformMonad CoreAlt
transalt (con, binders, expr) = do
expr' <- trans (Other:c) expr
return (con, binders, expr')
subeverywhere trans c (Var x) = return $ Var x
subeverywhere trans c (Lit x) = return $ Lit x
subeverywhere trans c (Type x) = return $ Type x
subeverywhere trans c (Cast expr ty) = do
expr' <- trans (Other:c) expr
return $ Cast expr' ty
subeverywhere trans c expr = error $ "\nNormalizeTools.subeverywhere: Unsupported expression: " ++ show expr
dotransforms :: [(String, Transform)] -> CoreExpr -> TranslatorSession CoreExpr
dotransforms transs expr = do
(expr', changed) <- Writer.runWriterT $ Monad.foldM (\e trans -> everywhere (apply trans) [] e) expr transs
if Monoid.getAny changed then dotransforms transs expr' else return expr'
inlinebind :: ((CoreBndr, CoreExpr) -> TransformMonad Bool) -> Transform
inlinebind condition context expr@(Let (Rec binds) res) = do
res_eithers <- mapM docond binds
case Either.partitionEithers res_eithers of
([], _) -> return expr
(replace, others) -> do
newexpr <- do_substitute replace (Let (Rec others) res)
change newexpr
where
docond :: (CoreBndr, CoreExpr) -> TransformMonad (Either (CoreBndr, CoreExpr) (CoreBndr, CoreExpr))
docond b = do
res <- condition b
return $ case res of True -> Left b; False -> Right b
do_substitute :: [(CoreBndr, CoreExpr)] -> CoreExpr -> TransformMonad CoreExpr
do_substitute [] expr = return expr
do_substitute ((bndr, val):reps) expr = do
expr' <- substitute_clone bndr val context expr
reps' <- mapM (subs_bind bndr val) reps
do_substitute reps' expr'
subs_bind :: CoreBndr -> CoreExpr -> (CoreBndr, CoreExpr) -> TransformMonad (CoreBndr, CoreExpr)
subs_bind bndr expr (b, v) = do
v' <- substitute_clone bndr expr (LetBinding:context) v
return (b, v')
inlinebind _ context expr = return expr
setChanged :: TransformMonad ()
setChanged = Writer.tell (Monoid.Any True)
change :: a -> TransformMonad a
change val = do
setChanged
return val
changeif :: Bool -> a -> TransformMonad a
changeif True val = change val
changeif False val = return val
substitute :: CoreBndr -> CoreExpr -> Transform
substitute find repl context expr = do
let subst = CoreSubst.extendSubst CoreSubst.emptySubst find repl
return $ CoreSubst.substExpr subst expr
substitute_clone :: CoreBndr -> CoreExpr -> Transform
substitute_clone find repl context (Var var) | find == var = do
repl' <- Trans.lift $ CoreTools.genUniques repl
change repl'
substitute_clone find repl context expr = subeverywhere (substitute_clone find repl) context expr
isRepr :: (CoreTools.TypedThing t) => t -> TransformMonad Bool
isRepr tything = Trans.lift (isRepr' tything)
isRepr' :: (CoreTools.TypedThing t) => t -> TranslatorSession Bool
isRepr' tything = case CoreTools.getType tything of
Nothing -> return False
Just ty -> MonadState.lift tsType $ VHDLTools.isReprType ty
is_local_var :: CoreSyn.CoreExpr -> TranslatorSession Bool
is_local_var (CoreSyn.Var v) = do
bndrs <- getGlobalBinders
return $ v `notElem` bndrs
is_local_var _ = return False
isUserDefined :: CoreSyn.CoreBndr -> Bool
isUserDefined bndr | Name.isSystemName (Id.idName bndr) = False
isUserDefined bndr = str `notElem` builtinIds
where
str = Name.getOccString bndr
isNormalizeable ::
Bool
-> CoreBndr
-> TranslatorSession Bool
isNormalizeable result_nonrep bndr = do
let ty = Id.idType bndr
let (arg_tys, res_ty) = Type.splitFunTys ty
let check_tys = if result_nonrep then arg_tys else (res_ty:arg_tys)
andM $ mapM isRepr' check_tys