1 {-# LANGUAGE PackageImports #-}
3 -- This module provides functions for program transformations.
5 module CLasH.Normalize.NormalizeTools where
8 import qualified Data.Monoid as Monoid
9 import qualified Control.Monad as Monad
10 import qualified Control.Monad.Trans.Writer as Writer
11 import qualified "transformers" Control.Monad.Trans as Trans
12 import qualified Data.Accessor.Monad.Trans.State as MonadState
19 import qualified CoreSubst
21 -- import qualified CoreUtils
22 -- import Outputable ( showSDoc, ppr, nest )
25 import CLasH.Normalize.NormalizeTypes
26 import CLasH.Translator.TranslatorTypes
27 import CLasH.VHDL.Constants (builtinIds)
29 import qualified CLasH.Utils.Core.CoreTools as CoreTools
30 import qualified CLasH.VHDL.VHDLTools as VHDLTools
32 -- Apply the given transformation to all expressions in the given expression,
33 -- including the expression itself.
34 everywhere :: (String, Transform) -> Transform
35 everywhere trans = applyboth (subeverywhere (everywhere trans)) trans
37 -- Apply the first transformation, followed by the second transformation, and
38 -- keep applying both for as long as expression still changes.
39 applyboth :: Transform -> (String, Transform) -> Transform
40 applyboth first (name, second) context expr = do
42 expr' <- first context expr
44 (expr'', changed) <- Writer.listen $ second context expr'
46 -- trace ("Trying to apply transform " ++ name ++ " to:\n" ++ showSDoc (nest 4 $ ppr expr') ++ "\nType: \n" ++ (showSDoc $ nest 4 $ ppr $ CoreUtils.exprType expr') ++ "\n")
49 -- trace ("Applying transform " ++ name ++ " to:\n" ++ showSDoc (nest 4 $ ppr expr') ++ "\nType: \n" ++ (showSDoc $ nest 4 $ ppr $ CoreUtils.exprType expr') ++ "\n"
50 -- ++ "Context: " ++ show context ++ "\n"
51 -- ++ "Result of applying " ++ name ++ ":\n" ++ showSDoc (nest 4 $ ppr expr'') ++ "\n" ++ "Type: \n" ++ (showSDoc $ nest 4 $ ppr $ CoreUtils.exprType expr'') ++ "\n" ) $
52 Trans.lift $ MonadState.modify tsTransformCounter (+1)
53 applyboth first (name, second) context expr''
55 -- trace ("No changes") $
58 -- Apply the given transformation to all direct subexpressions (only), not the
60 subeverywhere :: Transform -> Transform
61 subeverywhere trans c (App a b) = do
62 a' <- trans (AppFirst:c) a
63 b' <- trans (AppSecond:c) b
66 subeverywhere trans c (Let (NonRec b bexpr) expr) = do
67 bexpr' <- trans (LetBinding:c) bexpr
68 expr' <- trans (LetBody:c) expr
69 return $ Let (NonRec b bexpr') expr'
71 subeverywhere trans c (Let (Rec binds) expr) = do
72 expr' <- trans (LetBody:c) expr
73 binds' <- mapM transbind binds
74 return $ Let (Rec binds') expr'
76 transbind :: (CoreBndr, CoreExpr) -> TransformMonad (CoreBndr, CoreExpr)
78 e' <- trans (LetBinding:c) e
81 subeverywhere trans c (Lam x expr) = do
82 expr' <- trans (LambdaBody:c) expr
85 subeverywhere trans c (Case scrut b t alts) = do
86 scrut' <- trans (Other:c) scrut
87 alts' <- mapM transalt alts
88 return $ Case scrut' b t alts'
90 transalt :: CoreAlt -> TransformMonad CoreAlt
91 transalt (con, binders, expr) = do
92 expr' <- trans (Other:c) expr
93 return (con, binders, expr')
95 subeverywhere trans c (Var x) = return $ Var x
96 subeverywhere trans c (Lit x) = return $ Lit x
97 subeverywhere trans c (Type x) = return $ Type x
99 subeverywhere trans c (Cast expr ty) = do
100 expr' <- trans (Other:c) expr
101 return $ Cast expr' ty
103 subeverywhere trans c expr = error $ "\nNormalizeTools.subeverywhere: Unsupported expression: " ++ show expr
105 -- Runs each of the transforms repeatedly inside the State monad.
106 dotransforms :: [Transform] -> CoreExpr -> TranslatorSession CoreExpr
107 dotransforms transs expr = do
108 (expr', changed) <- Writer.runWriterT $ Monad.foldM (\e trans -> trans [] e) expr transs
109 if Monoid.getAny changed then dotransforms transs expr' else return expr'
111 -- Inline all let bindings that satisfy the given condition
112 inlinebind :: ((CoreBndr, CoreExpr) -> TransformMonad Bool) -> Transform
113 inlinebind condition context expr@(Let (NonRec bndr expr') res) = do
114 applies <- condition (bndr, expr')
117 -- Substitute the binding in res and return that
118 res' <- substitute_clone bndr expr' context res
121 -- Don't change this let
123 -- Leave all other expressions unchanged
124 inlinebind _ context expr = return expr
126 -- Sets the changed flag in the TransformMonad, to signify that some
127 -- transform has changed the result
128 setChanged :: TransformMonad ()
129 setChanged = Writer.tell (Monoid.Any True)
131 -- Sets the changed flag and returns the given value.
132 change :: a -> TransformMonad a
137 -- Returns the given value and sets the changed flag if the bool given is
138 -- True. Note that this will not unset the changed flag if the bool is False.
139 changeif :: Bool -> a -> TransformMonad a
140 changeif True val = change val
141 changeif False val = return val
143 -- | Creates a transformation that substitutes the given binder with the given
144 -- expression (This can be a type variable, replace by a Type expression).
145 -- Does not set the changed flag.
146 substitute :: CoreBndr -> CoreExpr -> Transform
147 -- Use CoreSubst to subst a type var in an expression
148 substitute find repl context expr = do
149 let subst = CoreSubst.extendSubst CoreSubst.emptySubst find repl
150 return $ CoreSubst.substExpr subst expr
152 -- | Creates a transformation that substitutes the given binder with the given
153 -- expression. This does only work for value expressions! All binders in the
154 -- expression are cloned before the replacement, to guarantee uniqueness.
155 substitute_clone :: CoreBndr -> CoreExpr -> Transform
156 -- If we see the var to find, replace it by a uniqued version of repl
157 substitute_clone find repl context (Var var) | find == var = do
158 repl' <- Trans.lift $ CoreTools.genUniques repl
161 -- For all other expressions, just look in subexpressions
162 substitute_clone find repl context expr = subeverywhere (substitute_clone find repl) context expr
164 -- Is the given expression representable at runtime, based on the type?
165 isRepr :: (CoreTools.TypedThing t) => t -> TransformMonad Bool
166 isRepr tything = Trans.lift (isRepr' tything)
168 isRepr' :: (CoreTools.TypedThing t) => t -> TranslatorSession Bool
169 isRepr' tything = case CoreTools.getType tything of
170 Nothing -> return False
171 Just ty -> MonadState.lift tsType $ VHDLTools.isReprType ty
173 is_local_var :: CoreSyn.CoreExpr -> TranslatorSession Bool
174 is_local_var (CoreSyn.Var v) = do
175 bndrs <- getGlobalBinders
176 return $ v `notElem` bndrs
177 is_local_var _ = return False
179 -- Is the given binder defined by the user?
180 isUserDefined :: CoreSyn.CoreBndr -> Bool
181 -- System names are certain to not be user defined
182 isUserDefined bndr | Name.isSystemName (Id.idName bndr) = False
183 -- Builtin functions are usually not user-defined either (and would
184 -- break currently if they are...)
185 isUserDefined bndr = str `notElem` builtinIds
187 str = Name.getOccString bndr
189 -- Is the given binder normalizable? This means that its type signature can be
190 -- represented in hardware, which should (?) guarantee that it can be made
191 -- into hardware. Note that if a binder is not normalizable, it might become
192 -- so using argument propagation.
193 isNormalizeable :: CoreBndr -> TransformMonad Bool
194 isNormalizeable bndr = Trans.lift (isNormalizeable' bndr)
196 isNormalizeable' :: CoreBndr -> TranslatorSession Bool
197 isNormalizeable' bndr = do
198 let ty = Id.idType bndr
199 let (arg_tys, res_ty) = Type.splitFunTys ty
200 -- This function is normalizable if all its arguments and return value are
202 andM $ mapM isRepr' (res_ty:arg_tys)