clash-0.1: CLasH/Utils/GhcTools.hs
{-# LANGUAGE ScopedTypeVariables #-}
module CLasH.Utils.GhcTools where
-- Standard modules
import qualified Monad
import qualified System.IO.Unsafe
import qualified Language.Haskell.TH as TH
import qualified Maybe
-- GHC API
import qualified Annotations
import qualified CoreSyn
import qualified CoreUtils
import qualified DynFlags
import qualified HscTypes
import qualified GHC
import qualified Name
import qualified Serialized
import qualified Var
import qualified Outputable
import qualified Class
-- Local Imports
import CLasH.Utils.Pretty
import CLasH.Translator.TranslatorTypes
import CLasH.Translator.Annotations
import CLasH.Utils
listBindings :: FilePath -> [FilePath] -> IO ()
listBindings libdir filenames = do
(cores,_,_) <- loadModules libdir filenames Nothing
let binds = concatMap (CoreSyn.flattenBinds . HscTypes.cm_binds) cores
mapM listBinding binds
putStr "\n=========================\n"
let classes = concatMap (HscTypes.typeEnvClasses . HscTypes.cm_types) cores
mapM listClass classes
return ()
listBinding :: (CoreSyn.CoreBndr, CoreSyn.CoreExpr) -> IO ()
listBinding (b, e) = do
putStr "\nBinder: "
putStr $ show b ++ "[" ++ show (Var.varUnique b) ++ "]"
putStr "\nType of Binder: \n"
putStr $ Outputable.showSDoc $ Outputable.ppr $ Var.varType b
putStr "\n\nExpression: \n"
putStr $ prettyShow e
putStr "\n\n"
putStr $ Outputable.showSDoc $ Outputable.ppr e
putStr "\n\nType of Expression: \n"
putStr $ Outputable.showSDoc $ Outputable.ppr $ CoreUtils.exprType e
putStr "\n\n"
listClass :: Class.Class -> IO ()
listClass c = do
putStr "\nClass: "
putStr $ show (Class.className c)
putStr "\nSelectors: "
putStr $ show (Class.classSelIds c)
putStr "\n"
-- | Show the core structure of the given binds in the given file.
listBind :: FilePath -> [FilePath] -> String -> IO ()
listBind libdir filenames name = do
(cores,_,_) <- loadModules libdir filenames Nothing
bindings <- concatM $ mapM (findBinder (hasVarName name)) cores
mapM_ listBinding bindings
return ()
-- Change a DynFlag from within the Ghc monad. Strangely enough there seems to
-- be no standard function to do exactly this.
setDynFlag :: DynFlags.DynFlag -> GHC.Ghc ()
setDynFlag dflag = do
dflags <- GHC.getSessionDynFlags
let dflags' = DynFlags.dopt_set dflags dflag
GHC.setSessionDynFlags dflags'
return ()
-- We don't want the IO monad sprinkled around everywhere, so we hide it.
-- This should be safe as long as we only do simple things in the GhcMonad
-- such as interface lookups and evaluating simple expressions that
-- don't have side effects themselves (Or rather, that don't use
-- unsafePerformIO themselves, since normal side effectful function would
-- just return an IO monad when they are evaluated).
unsafeRunGhc :: FilePath -> GHC.Ghc a -> a
unsafeRunGhc libDir m =
System.IO.Unsafe.unsafePerformIO $
GHC.runGhc (Just libDir) $ do
dflags <- GHC.getSessionDynFlags
GHC.setSessionDynFlags dflags
m
-- | Loads the given files and turns it into a core module
loadModules ::
FilePath -- ^ The GHC Library directory
-> [String] -- ^ The files that need to be loaded
-> Maybe Finder -- ^ What entities to build?
-> IO ( [HscTypes.CoreModule]
, HscTypes.HscEnv
, [EntitySpec]
) -- ^ ( The loaded modules, the resulting ghc environment, the entities to build)
loadModules libdir filenames finder =
GHC.defaultErrorHandler DynFlags.defaultDynFlags $
GHC.runGhc (Just libdir) $ do
dflags <- GHC.getSessionDynFlags
GHC.setSessionDynFlags dflags
cores <- mapM GHC.compileToCoreModule filenames
env <- GHC.getSession
specs <- case finder of
Nothing -> return []
Just f -> concatM $ mapM f cores
return (cores, env, specs)
findBinds ::
Monad m =>
(Var.Var -> m Bool)
-> HscTypes.CoreModule
-> m (Maybe [CoreSyn.CoreBndr])
findBinds criteria core = do
binders <- findBinder criteria core
case binders of
[] -> return Nothing
bndrs -> return $ Just $ map fst bndrs
findBind ::
Monad m =>
(Var.Var -> m Bool)
-> HscTypes.CoreModule
-> m (Maybe CoreSyn.CoreBndr)
findBind criteria core = do
binders <- findBinds criteria core
case binders of
Nothing -> return Nothing
(Just bndrs) -> return $ Just $ head bndrs
findExprs ::
Monad m =>
(Var.Var -> m Bool)
-> HscTypes.CoreModule
-> m (Maybe [CoreSyn.CoreExpr])
findExprs criteria core = do
binders <- findBinder criteria core
case binders of
[] -> return Nothing
bndrs -> return $ Just (map snd bndrs)
findExpr ::
Monad m =>
(Var.Var -> m Bool)
-> HscTypes.CoreModule
-> m (Maybe CoreSyn.CoreExpr)
findExpr criteria core = do
exprs <- findExprs criteria core
case exprs of
Nothing -> return Nothing
(Just exprs) -> return $ Just $ head exprs
findAnns ::
Monad m =>
(Var.Var -> m [CLasHAnn])
-> HscTypes.CoreModule
-> m [CLasHAnn]
findAnns criteria core = do
let binds = CoreSyn.flattenBinds $ HscTypes.cm_binds core
anns <- Monad.mapM (criteria . fst) binds
case anns of
[] -> return []
xs -> return $ concat xs
-- | Find a binder in module according to a certain criteria
findBinder ::
Monad m =>
(Var.Var -> m Bool) -- ^ The criteria to filter the binders on
-> HscTypes.CoreModule -- ^ The module to be inspected
-> m [(CoreSyn.CoreBndr, CoreSyn.CoreExpr)] -- ^ The binders to meet the criteria
findBinder criteria core = do
let binds = CoreSyn.flattenBinds $ HscTypes.cm_binds core
Monad.filterM (criteria . fst) binds
-- | Determine if a binder has an Annotation meeting a certain criteria
isCLasHAnnotation ::
GHC.GhcMonad m =>
(CLasHAnn -> Bool) -- ^ The criteria the Annotation has to meet
-> Var.Var -- ^ The Binder
-> m [CLasHAnn] -- ^ Indicates if binder has the Annotation
isCLasHAnnotation clashAnn var = do
let deserializer = Serialized.deserializeWithData
let target = Annotations.NamedTarget (Var.varName var)
(anns :: [CLasHAnn]) <- GHC.findGlobalAnns deserializer target
let annEnts = filter clashAnn anns
return annEnts
-- | Determine if a binder has an Annotation meeting a certain criteria
hasCLasHAnnotation ::
GHC.GhcMonad m =>
(CLasHAnn -> Bool) -- ^ The criteria the Annotation has to meet
-> Var.Var -- ^ The Binder
-> m Bool -- ^ Indicates if binder has the Annotation
hasCLasHAnnotation clashAnn var = do
anns <- isCLasHAnnotation clashAnn var
case anns of
[] -> return False
xs -> return True
-- | Determine if a binder has a certain name
hasVarName ::
Monad m =>
String -- ^ The name the binder has to have
-> Var.Var -- ^ The Binder
-> m Bool -- ^ Indicate if the binder has the name
hasVarName lookfor bind = return $ lookfor == Name.occNameString (Name.nameOccName $ Name.getName bind)
findInitStates ::
(Var.Var -> GHC.Ghc Bool) ->
(Var.Var -> GHC.Ghc [CLasHAnn]) ->
HscTypes.CoreModule ->
GHC.Ghc (Maybe [(CoreSyn.CoreBndr, CoreSyn.CoreBndr)])
findInitStates statec annsc mod = do
states <- findBinds statec mod
anns <- findAnns annsc mod
let funs = Maybe.catMaybes (map extractInits anns)
exprs' <- mapM (\x -> findBind (hasVarName (TH.nameBase x)) mod) funs
let exprs = Maybe.catMaybes exprs'
let inits = zipMWith (\a b -> (a,b)) states exprs
return inits
where
extractInits :: CLasHAnn -> Maybe TH.Name
extractInits (InitState x) = Just x
extractInits _ = Nothing
zipMWith :: (a -> b -> c) -> (Maybe [a]) -> [b] -> (Maybe [c])
zipMWith _ Nothing _ = Nothing
zipMWith f (Just as) bs = Just $ zipWith f as bs
-- | Make a complete spec out of a three conditions
findSpec ::
(Var.Var -> GHC.Ghc Bool) -> (Var.Var -> GHC.Ghc Bool) -> (Var.Var -> GHC.Ghc [CLasHAnn]) -> (Var.Var -> GHC.Ghc Bool)
-> Finder
findSpec topc statec annsc testc mod = do
top <- findBind topc mod
state <- findExprs statec mod
anns <- findAnns annsc mod
test <- findExpr testc mod
inits <- findInitStates statec annsc mod
return [(top, inits, test)]
-- case top of
-- Just t -> return [(t, state, test)]
-- Nothing -> return error $ "Could not find top entity requested"