{-# LANGUAGE GeneralizedNewtypeDeriving, FlexibleContexts, OverloadedStrings #-}
module Futhark.Pipeline
( Pipeline
, PipelineConfig (..)
, Action (..)
, FutharkM
, runFutharkM
, Verbosity(..)
, internalErrorS
, module Futhark.Error
, onePass
, passes
, runPasses
, runPipeline
)
where
import Control.Category
import Control.Monad
import Control.Monad.Writer.Strict hiding (pass)
import Control.Monad.Except
import Control.Monad.State
import Control.Monad.Reader
import qualified Data.Text as T
import qualified Data.Text.IO as T
import Data.Time.Clock
import System.IO
import Text.Printf
import Prelude hiding (id, (.))
import Futhark.Error
import Futhark.Representation.AST (Prog, PrettyLore)
import Futhark.TypeCheck
import Futhark.Pass
import Futhark.Util.Log
import Futhark.Util.Pretty (Pretty, prettyText)
import Futhark.MonadFreshNames
data Verbosity = NotVerbose | Verbose | VeryVerbose deriving (Eq, Ord)
newtype FutharkEnv = FutharkEnv { futharkVerbose :: Verbosity }
data FutharkState = FutharkState { futharkPrevLog :: UTCTime
, futharkNameSource :: VNameSource }
newtype FutharkM a = FutharkM (ExceptT CompilerError (StateT FutharkState (ReaderT FutharkEnv IO)) a)
deriving (Applicative, Functor, Monad,
MonadError CompilerError,
MonadState FutharkState,
MonadReader FutharkEnv,
MonadIO)
instance MonadFreshNames FutharkM where
getNameSource = gets futharkNameSource
putNameSource src = modify $ \s -> s { futharkNameSource = src }
instance MonadLogger FutharkM where
addLog = mapM_ perLine . T.lines . toText
where perLine msg = do
verb <- asks $ (>=Verbose) . futharkVerbose
prev <- gets futharkPrevLog
now <- liftIO getCurrentTime
let delta :: Double
delta = fromRational $ toRational (now `diffUTCTime` prev)
prefix = printf "[ +%.6f] " delta
modify $ \s -> s { futharkPrevLog = now }
when verb $ liftIO $ T.hPutStrLn stderr $ T.pack prefix <> msg
runFutharkM :: FutharkM a -> Verbosity -> IO (Either CompilerError a)
runFutharkM (FutharkM m) verbose = do
s <- FutharkState <$> getCurrentTime <*> pure blankNameSource
runReaderT (evalStateT (runExceptT m) s) newEnv
where newEnv = FutharkEnv verbose
internalErrorS :: Pretty t => String -> t -> FutharkM a
internalErrorS s p = throwError $ InternalError (T.pack s) (prettyText p) CompilerBug
data Action lore =
Action { actionName :: String
, actionDescription :: String
, actionProcedure :: Prog lore -> FutharkM ()
}
data PipelineConfig =
PipelineConfig { pipelineVerbose :: Bool
, pipelineValidate :: Bool
}
newtype Pipeline fromlore tolore =
Pipeline { unPipeline :: PipelineConfig -> Prog fromlore -> FutharkM (Prog tolore) }
instance Category Pipeline where
id = Pipeline $ const return
p2 . p1 = Pipeline perform
where perform cfg prog =
runPasses p2 cfg =<< runPasses p1 cfg prog
runPasses :: Pipeline fromlore tolore
-> PipelineConfig
-> Prog fromlore
-> FutharkM (Prog tolore)
runPasses = unPipeline
runPipeline :: Pipeline fromlore tolore
-> PipelineConfig
-> Prog fromlore
-> Action tolore
-> FutharkM ()
runPipeline p cfg prog a = do
prog' <- runPasses p cfg prog
when (pipelineVerbose cfg) $ logMsg $
"Running action " <> T.pack (actionName a)
actionProcedure a prog'
onePass :: (Checkable fromlore, Checkable tolore) =>
Pass fromlore tolore -> Pipeline fromlore tolore
onePass pass = Pipeline perform
where perform cfg prog = do
when (pipelineVerbose cfg) $ logMsg $
"Running pass " <> T.pack (passName pass)
prog' <- runPass pass prog
when (pipelineValidate cfg) $
case checkProg prog' of
Left err -> validationError pass prog' $ show err
Right () -> return ()
return prog'
passes :: Checkable lore =>
[Pass lore lore] -> Pipeline lore lore
passes = foldl (>>>) id . map onePass
validationError :: PrettyLore tolore =>
Pass fromlore tolore -> Prog tolore -> String -> FutharkM a
validationError pass prog err =
throwError $ InternalError msg (prettyText prog) CompilerBug
where msg = "Type error after pass '" <> T.pack (passName pass) <> "':\n" <> T.pack err
runPass :: PrettyLore fromlore =>
Pass fromlore tolore
-> Prog fromlore
-> FutharkM (Prog tolore)
runPass pass prog = do
(res, logged) <- runPassM (passFunction pass prog)
verb <- asks $ (>=VeryVerbose) . futharkVerbose
when verb $ addLog logged
case res of Left err -> internalError err $ prettyText prog
Right x -> return x