packages feed

signals-0.0.0.1: Backend/Compiler/Compiler.hs

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE Rank2Types          #-}
{-# LANGUAGE GADTs               #-}
{-# LANGUAGE KindSignatures      #-}
{-# LANGUAGE FlexibleContexts    #-}
{-# LANGUAGE TypeOperators       #-}

module Backend.Compiler.Compiler (
    compiler
  , inspect_compiler
  )
where

import           Core (CMD, EEq(..))
import qualified Core as C

import           Frontend.Stream (Stream, Str)
import qualified Frontend.Stream as Str

import           Frontend.Signal (Signal, Sig, Struct(..), TStruct(..), Empty)
import qualified Frontend.Signal as S

import Frontend.SignalObsv (TSignal(..), Node, edges)

import Backend.Ex
import Backend.Compiler.Cycles
import Backend.Compiler.Linker
import Backend.Compiler.Sorter

import Control.Monad.Reader
import Control.Monad.State hiding (State)
import Control.Monad.Operational

import Data.Typeable
import Data.Reify (Unique, Graph(..), reifyGraph)
import Data.Maybe (fromJust)
import Data.List  (sortBy, mapAccumR)
import Data.Traversable (traverse)
import Data.Function (on)

import           Data.Map (Map, (!))
import qualified Data.Map as M

import Prelude hiding (reads)

--------------------------------------------------------------------------------
-- *
--------------------------------------------------------------------------------

-- | Shorthand for programs using 'CMD' as their instruction set
type Prog exp = Program (CMD exp)

--------------------------------------------------------------------------------

compiler :: ( Typeable exp, Typeable a, Typeable b
            , EEq exp Int, Num (exp Int), Integral (exp Int)
            )
         =>    (Sig exp a -> Sig exp b)
         -> IO (Str exp a -> Str exp b)
compiler f =
  do (Graph nodes root) <- reifyGraph f

     let links = linker nodes
         order = sorter root nodes
         cycle = cycles root nodes

     return $ case cycle of
       True  -> error "found cycle in graph"
       False -> compiler' nodes links order False

--------------------------------------------------------------------------------
-- * Channels
--------------------------------------------------------------------------------

-- | Binary trees over references
data RStruct exp a
  where
    RLeaf :: Typeable a => C.Ref (exp a) -> RStruct exp (Empty (exp a))
    RPair :: RStruct exp a -> RStruct exp b -> RStruct exp (a, b)

-- | Untyped binary trees over references
type REx exp = Ex (RStruct exp)

-- | ...
data Channel symbol exp = C {
    _ch_in  :: Map symbol (REx exp)
  , _ch_out :: Map symbol (REx exp)
  }

--------------------------------------------------------------------------------
-- hacky solution for now

-- |
initChannels :: (Ord s, Read s, Typeable e) => Resolution s e -> Prog e (Channel s e)
initChannels res = do
  outs <- M.traverseWithKey (const makeChannel) $ _output res
  return $ C {
    _ch_in  = M.map (copyChannel outs) $ _input res
  , _ch_out = outs
  }

-- |
makeChannel :: TEx e -> Prog e (REx e)
makeChannel (Ex s) = makes s >>= return . Ex
  where
    makes :: TStruct e a -> Prog e (RStruct e a)
    makes (TLeaf _)   = C.initRef >>= return . RLeaf
    makes (TPair r l) = do
      r' <- makes r
      l' <- makes l
      return $ RPair r' l'

-- |
copyChannel :: forall e s. (Ord s, Read s, Typeable e) => Map s (REx e) -> TEx e -> REx e
copyChannel m (Ex s) = Ex $ copys s
  where
    copys :: TStruct e a -> RStruct e a
    copys (TLeaf i)   = case m ! read i of (Ex (RLeaf r)) -> case gcast r of Just x -> RLeaf x
    copys (TPair l r) = RPair (copys l) (copys r)

--------------------------------------------------------------------------------
-- * Compiler
--------------------------------------------------------------------------------

-- | ...
data Enviroment symbol exp = Env
  { _links    :: Resolution symbol exp
  , _channels :: Channel    symbol exp 
  , _firsts   :: Map symbol (Ex (C.Ref :*: exp)) -- merge with _channels
  , _buffers  :: Map symbol (Ex (Buffer exp))
  , _inputs   :: Ex (Prog exp :*: exp)
--, ...
  }

-- | 
type Type exp = ReaderT (Enviroment Unique exp) (Prog exp)

--------------------------------------------------------------------------------

reads :: RStruct exp a -> Prog exp (Struct exp a)
reads (RLeaf r)   = C.unsafeGetRef r >>= return . Leaf
reads (RPair l r) = do
  l' <- reads l
  r' <- reads r
  return $ Pair l' r'

writes :: Struct exp a -> RStruct exp a -> Prog exp ()
writes (Leaf s)   (RLeaf r)   = C.setRef r s
writes (Pair l r) (RPair u v) = writes l u >> writes r v

--------------------------------------------------------------------------------

-- | Read
read_in :: Typeable a => Unique -> TStruct exp a -> Type exp (Struct exp a)
read_in u _ =
  do (Ex ch) <- asks ((! u) . _ch_in . _channels)
     case gcast ch of
       Just s  -> lift $ reads s
       Nothing -> error "hepa: type error"

-- | Read 
read_out :: Typeable a => Unique -> TStruct exp a -> Type exp (Struct exp a)
read_out u _ =
  do (Ex ch) <- asks ((! u) . _ch_out . _channels)
     case gcast ch of
       Just s  -> lift $ reads s
       Nothing -> error "bepa: type error"

-- | Write
write_out :: Typeable a => Unique -> Struct exp a -> Type exp ()
write_out u s =
  do (Ex ch) <- asks ((! u) . _ch_out . _channels)
     case gcast ch of
       Just r  -> lift $ writes s r
       Nothing -> error "depa: type error"

--------------------------------------------------------------------------------

read_buffer :: (Typeable a, Num (exp Int)) => Unique -> Type exp (exp a)
read_buffer u =
  do (Ex buff) <- asks ((! u) . _buffers)
     case gcast buff of
       Just b  -> lift $ getBuff b
       Nothing -> error "apa: type error"

write_buffer :: forall exp. Typeable exp => Unique -> Type exp ()
write_buffer u =
  do (Ex (buff :: Buffer exp a)) <- asks ((! u) . _buffers)
     (Leaf e) <- read_out u (undefined :: TStruct exp (Empty (exp a)))
     lift $ putBuff buff e

--------------------------------------------------------------------------------

-- | ...
compile :: (Typeable exp, Num (exp Int)) => (Unique, Node exp) -> Type exp ()
compile (i, TVar t@(TLeaf _)) =
  do input <- asks (apa t . _inputs)
     value <- lift $ liftProgram input
     write_out i (Leaf value)
  where
    apa :: Typeable e => TStruct exp (Empty (exp e)) -> Ex (f :*: g) -> f (g e)
    apa _ = unwrap

compile (i, TConst c) =
  do value <- lift $ liftProgram $ Str.run c
     write_out i (Leaf value)

compile (i, TLift (f :: Stream exp (exp a) -> Stream exp (exp b)) _) =
  do let t = undefined :: TStruct exp (Empty (exp a))
     (Leaf input) <- read_in i t
     value <- lift $ liftProgram $ Str.run $ f $ Str.repeat input
     write_out i (Leaf value)

-- I could remove the extra variable (value), todo...
compile (i, TDelay (e :: exp a) _) =
  do first  <- asks (unwrap . (! i) . _firsts) :: Type exp (C.Ref (exp a))
     output <- lift $ C.unsafeGetRef first
     write_out i (Leaf output)
{-
  do let t = undefined :: TStruct exp (Empty (exp a))
     (Leaf input) <- read_in i t
     first <- asks (unwrap . (! i) . _firsts) :: Type exp (C.Ref (exp a))
     value <- lift $ liftProgram $
                do output <- C.unsafeGetRef first
                   C.setRef first input
                   return output
     write_out i (Leaf value)
-}
compile (i, TBuff (_ :: proxy (exp a)) u) =
  do value <- read_buffer u :: Type exp (exp a)
     write_out i (Leaf value)

compile (i, TMap ti to f _) =
  do input <- read_in i ti
     value <- return $ f input
     write_out i value
     
compile _ = return ()

--------------------------------------------------------------------------------

-- | ...
compiler' :: forall exp a b.
             ( Typeable exp, Typeable a, Typeable b
             , EEq exp Int, Num (exp Int), Integral (exp Int)
             )
          => [(Unique, Node exp)]
          -> Resolution Unique exp
          -> Map Unique Order
          -> Bool
          -> (Stream exp (exp a) -> Stream exp (exp b))
compiler' nodes links order opt input = Str.stream $
  do (nodes', buffers) <- if opt then opt_delay_chains nodes else return (nodes, M.empty)
     env               <- init (Str.run input) buffers
     return $
       do let t      = undefined :: TStruct exp (Empty (exp b))
              delays = [ d | d@(_, TDelay {}) <- nodes]
              sorted = sort   nodes'
              last   = final  sorted
              keys   = M.keys buffers
              
          (Leaf value) <- flip runReaderT env $
            do mapM_ compile sorted
               forM_ keys    write_buffer
               forM_ delays  update_delay
               read_out last t

          return value

  where
    -- Create initial eviroment
    init :: Prog exp (exp a) -> Map Unique (Ex (Buffer exp)) -> Prog exp (Enviroment Unique exp)
    init i b =
      do let delays = M.fromList [ d | d@(_, TDelay {}) <- nodes]
             fnodes = map fst $ filterNOP nodes
             flinks = Resolution {
                 _output = M.filterWithKey (\k _ -> k `elem` fnodes) $ _output links
               , _input  = M.filterWithKey (\k _ -> k `elem` fnodes) $ _input  links
               }
         firsts   <- M.traverseWithKey (const $ init_delay) delays
         channels <- initChannels flinks
         return $ Env {
             _links    = links
           , _channels = channels
           , _firsts   = firsts
           , _buffers  = b
           , _inputs   = wrap i
           }

    -- ...
    init_delay :: Node exp -> Prog exp (Ex (C.Ref :*: exp))
    init_delay (TDelay d _) = C.newRef d >>= return . wrap

    -- ...
    update_delay :: (Unique, Node exp) -> Type exp ()
    update_delay (i, TDelay (e :: exp x) _) =
      do first <- asks (unwrap . (! i) . _firsts) :: Type exp (C.Ref (exp a))
         (Leaf input) <- read_in i (undefined :: TStruct exp (Empty (exp a)))
         lift $ liftProgram $ C.setRef first input

    -- Sort graph nodes by the given ordering
    sort :: [(Unique, Node exp)] -> [(Unique, Node exp)]
    sort = fmap (fmap snd) . sortBy (compare `on` (fst . snd))
         . M.toList . M.intersectionWith (,) order
         . M.fromList

    -- Find final reference to read output from
    final :: [(Unique, Node exp)] -> Unique
    final = fst . last . filterNOP

    -- Filter unused nodes
    filterNOP :: [(Unique, Node exp)] -> [(Unique, Node exp)]
    filterNOP = filter (not . nop . snd)
      where nop (TLambda {}) = True
            nop (TZip {})    = True
            nop (TFst {})    = True
            nop (TSnd {})    = True
            nop _            = False

--------------------------------------------------------------------------------
-- * Buffers
--------------------------------------------------------------------------------

data Buffer exp a = Buffer
  { getBuff :: Program (CMD exp) (exp a)
  , putBuff :: exp a -> Program (CMD exp) ()
  }

newBuff :: forall exp a. (EEq exp Int, Num (exp Int), Integral (exp Int))
        => exp Int -> exp a -> Prog exp (Buffer exp a)
newBuff size init =
  do arr <- C.newArr size init
     ir  <- C.newRef (0 :: exp Int)

     let get = do i <- C.unsafeGetRef ir
                  C.iff (i ==: 0)
                        (C.setRef ir size)
                        (C.setRef ir (i - 1))
                  C.getArr i arr
     
     let put a = do i <- C.unsafeGetRef ir
                    C.setArr i a arr
                    C.iff (i ==: size)
                          (C.setRef ir 0)
                          (C.setRef ir (i + 1))

     return $ Buffer get put

--------------------------------------------------------------------------------

-- | For each node in the given list, it finds any chain of delays associated
--   with the node and returns a mapping over each chained node and its chain
find_chains :: [(Unique, Node e)] -> Map Unique [(Unique, Node e)]
find_chains nodes = 
  let delays = M.foldrWithKey (\k n -> M.insert (head $ edges n) (k, n)) M.empty
             $ M.fromList [ d | d@(i, TDelay {}) <- nodes ]
      heads  = M.foldr (M.delete . fst) delays delays
  in  M.filter ((>1) . length) $ M.map (flip chain delays) heads
  where
    chain v@(i, _) m = v : maybe [] (flip chain m) (M.lookup i m)

-- | 
buffer_chains :: forall e. (EEq e Int, Integral (e Int), Num (e Int))
              => Map Unique [(Unique, Node e)]          -- original chains
              -> Prog e ( Map Unique [(Unique, Node e)] -- updated chains
                        , Map Unique (Ex (Buffer e))    -- buffers
                        )
buffer_chains chains = 
  do let values  = M.map        (map val)   chains
         chains' = M.mapWithKey (map . acc) chains

     -- Since newBuff fills the entire array with the same value,
     -- we only use the first value of the delay chains.
     -- This should be fixed!
     buffers <- traverse (\x@((Ex v):_) ->
                      do buff <- newBuff (fromIntegral $ length x) v
                         return (Ex buff)
                    )
                  values
     
     return (chains', buffers)
  where
    val   (i, TDelay v _) = Ex v
    acc k (i, TDelay v _) = (i, TBuff (apa v) k)
      where apa :: exp a -> Proxy (exp a)
            apa _ = Proxy::Proxy (exp a)

-- | Replaces all original nodes with the updated chain versions
replace_chains :: [(Unique, Node e)] -> Map Unique [(Unique, Node e)] -> [(Unique, Node e)]
replace_chains nodes = M.toList . M.foldr (flip $ foldr $ uncurry M.insert) (M.fromList nodes)

--------------------------------------------------------------------------------

-- | ...
--
-- We assume that:
--   * Each delay chains values are of the same type
--   * ...
opt_delay_chains :: (EEq e Int, Num (e Int), Integral (e Int))
                 => [(Unique, Node e)]
                 -> Prog e ( [(Unique, Node e)]
                           , Map Unique (Ex (Buffer e))
                           )
opt_delay_chains nodes =
  do (chains, buffers) <- buffer_chains $ find_chains nodes
     return (replace_chains nodes chains, buffers)

--------------------------------------------------------------------------------
-- * Testing
--------------------------------------------------------------------------------

inspect_compiler :: ( Typeable exp, Typeable a, Typeable b
                    , EEq exp Int, Num (exp Int), Integral (exp Int)
                    )
                 =>    (Sig exp a -> Sig exp b)
                 -> IO (Str exp a -> Str exp b)
inspect_compiler f =
  do (Graph nodes root) <- reifyGraph f

     let links = linker nodes
         order = sorter root nodes
         cycle = cycles root nodes
     
     putStrLn "=================================================="
     putStrLn "= Inspecting Compiler"
     putStrLn "=================================================="
     putStrLn "- Nodes"
     putStrLn "--------------------------------------------------"
     putStrLn $ show nodes
     putStrLn "--------------------------------------------------"
     putStrLn "- Order"
     putStrLn "--------------------------------------------------"
     putStrLn $ show order
     putStrLn "--------------------------------------------------"
     putStrLn "- Input Links"
     putStrLn "--------------------------------------------------"
     putStrLn $ show $ _input links
     putStrLn "--------------------------------------------------"
     putStrLn "- Output Links"
     putStrLn "--------------------------------------------------"
     putStrLn $ show $ _output links
     putStrLn "--------------------------------------------------"

     return $ \input -> case cycle of
       True  -> error "found cycle in graph"
       False -> compiler' nodes links order True input

--------------------------------------------------------------------------------
                                 
m !? i = case M.lookup i m of
           Just x  -> x
           Nothing -> error $ "Can't find key " ++ show i ++
                              " in map: \n"     ++ show m