packages feed

lhc-0.10: src/Grin/HPT/Solve.hs

{-# LANGUAGE OverloadedStrings #-}
module Grin.HPT.Solve
    ( solve
    ) where

import Grin.Types

import qualified Data.Map as Map
import qualified Data.Set as Set
import Control.Monad.Reader
import Control.Monad.Writer

import Grin.HPT.Environment
import qualified Grin.HPT.Interface as Interface

data HeapAnalysis
    = HeapAnalysis (Map.Map Lhs Rhs) SharingMap

instance Show HeapAnalysis where
    show (HeapAnalysis eqs _sharingMap)
        = unlines [ show lhs ++ " = " ++ show rhs | (lhs,rhs) <- Map.toList eqs ]

type SharingMap = Map.Map Lhs Bool

type M a = ReaderT (Equations,SharingMap) (Writer (Endo Equations, Endo SharingMap)) a


dataOne = singleton (Tag (Anonymous 2) ConstructorNode 0 [arg])
arg = singleton (Tag (Anonymous 3) ConstructorNode 0 [])
testEqs = Map.fromList [(VarEntry (Anonymous 1), dataOne)
                       ,(VarEntry (Anonymous 4), singleton (Ident (Anonymous 1)))
                       ,(VarEntry (Anonymous 5), singleton (Ident (Anonymous 4)))
                       ,(VarEntry (Anonymous 6), singleton (Ident (Anonymous 4)))]

mkInterface :: HeapAnalysis -> Interface.HeapAnalysis
mkInterface (HeapAnalysis binds smap)
    = Interface.mkHeapAnalysis (Map.map fromRhs binds) smap
    where fromRhs (Rhs vals) = mconcat (map toRhs vals)
          toRhs Base = Interface.Base
          toRhs (Heap hp) = Interface.Other Map.empty [] (Set.singleton hp)
          toRhs (Tag node nt missing args) = Interface.Other (Map.singleton (node,nt,missing) (map fromRhs args)) [] Set.empty
          toRhs (VectorTag rhs) = Interface.Other Map.empty (map fromRhs rhs) Set.empty
          toRhs rhs = error $ "Grin.HPT.Solve.mkInterface: bad rhs: " ++ show rhs

solve :: Equations -> (Int, Interface.HeapAnalysis)
solve eqs
    = case solve' eqs of
        (iterations, hpt) -> (iterations, mkInterface hpt)

solve' :: Equations -> (Int, HeapAnalysis)
solve' eqs
    = let iterate ls
              = forM_ ls $ \(lhs,rhs) ->
                  do reducedRhs <- reduceEqs rhs
                     addReduced lhs reducedRhs
          loop iter shared prev
              = case execWriter (runReaderT (iterate (Map.toList eqs)) (prev, shared)) of
                  (newDefs, newShared) ->
                    let next = appEndo newDefs prev
                        nextShared = appEndo newShared shared
                    in if prev == next then (iter, HeapAnalysis next nextShared) else loop (iter+1) nextShared next
      in loop 1 (nonlinearVariables eqs) (Map.map (const mempty) eqs)

-- Scan for shared variables. A variable is shared if it is used more than once.
-- Detecting shared heap points is done later when we solve the equations.
nonlinearVariables :: Equations -> SharingMap
nonlinearVariables eqs
    = appEndo (execWriter (mapM_ rhsFn (Map.elems eqs))) Map.empty
    where rhsFn (Rhs values) = mapM_ worker values
          pushIdent ident = tell $ Endo $ Map.insertWith (\_ _ -> True) (VarEntry ident) False
          worker (Extract ident (tag, _nt, _missing) _nth)   = pushIdent ident >> pushIdent tag
          worker (ExtractVector ident _nth) = pushIdent ident
          worker (Eval ident)               = pushIdent ident
          worker (Update a b)               = pushIdent a >> pushIdent b
          worker (Apply a b)                = pushIdent a >> pushIdent b
          worker (PartialApply a b)         = return ()
          worker (Ident ident)              = pushIdent ident
          worker (Fetch ident)              = pushIdent ident
          worker Base                       = return ()
          worker Heap{}                     = return ()
          worker (Tag tag _nt _nargs args)  = pushIdent tag >> mapM_ rhsFn args
          worker (VectorTag args)           = mapM_ rhsFn args

addReduced :: Lhs -> Rhs -> M ()
addReduced lhs rhs
    = do orig <- lookupEq lhs
         unless (rhs `isSubsetOf` orig) $
           do tell (Endo $ Map.insertWith mappend lhs rhs, mempty)
              shared <- isShared lhs
              when shared $
                mapM_ setShared (listHeapPointers rhs)

listHeapPointers :: Rhs -> [HeapPointer]
listHeapPointers rhs = workerRhs rhs []
    where workerRhs (Rhs values)            = flip (foldr worker) values
          worker (Heap hp)                  = (hp:)
          worker (Tag _tag _nt _nargs args) = flip (foldr workerRhs) args
          worker (VectorTag args)           = flip (foldr workerRhs) args
          worker _                          = id


reduceEqs :: Rhs -> M Rhs
reduceEqs (Rhs rhs) = do rhs' <- mapM reduceEq rhs
                         return $ mconcat rhs'

reduceEq :: RhsValue -> M Rhs
reduceEq Base      = return $ singleton Base
reduceEq (Heap hp) = return $ singleton $ Heap hp
reduceEq (Ident i) = lookupEq (VarEntry i)
reduceEq (Extract eq (tag, _nt, _missing) n)
    = do Rhs eqs' <- lookupEq (VarEntry eq)
         return ({-# SCC "Extract.mappend" #-} mconcat [ args `nth` n | Tag t _ _ args <- eqs', t == tag ])
    where nth [] n = mempty --error $ "reduceEq: ExtractVector: " ++ show (eqs, tag, n)
          nth (x:xs) 0 = x
          nth (x:xs) n = nth xs (n-1)
reduceEq (ExtractVector eq n)
    = do Rhs eqs' <- lookupEq (VarEntry eq)
         return ({-# SCC "ExtractVector.mappend" #-} mconcat [ args `nth` n | VectorTag args <- eqs' ])
    where nth [] n = error $ "reduceEq: ExtractVector: " ++ show (eq, n)
          nth (x:xs) 0 = x
          nth (x:xs) n = nth xs (n-1)
reduceEq (Tag t nt missing args)
    = do args' <- mapM reduceEqs args
         return $ singleton (Tag t nt missing args')
reduceEq (VectorTag args)
    = do args' <- mapM reduceEqs args
         return $ singleton (VectorTag args')
reduceEq (Eval i)
    = do Rhs vals <- lookupEq (VarEntry i)
         let unHeap (Heap hp) = hp
             unHeap t         = error $ "reduceEq: eval: " ++ show (t,i,vals)
             hps = map unHeap vals
         anyShared <- liftM or $ mapM (isShared . HeapEntry) hps
         let fn hp = do Rhs rhs <- lookupEq (HeapEntry hp)
                        let worker (Tag fn FunctionNode 0 _) = do rhs <- lookupEq (VarEntry fn)
                                                                  when (anyShared && rhs /= mempty) $
                                                                    addReduced (HeapEntry hp) rhs
                                                                  return rhs
                            worker other = return $ singleton other
                        {-# SCC "Eval.mappend2" #-} liftM mconcat $ mapM worker rhs
         {-# SCC "Eval.mappend" #-} liftM mconcat $ mapM fn hps
reduceEq (Fetch i)
    = do Rhs vals <- lookupEq (VarEntry i)
         let f (Heap hp) = lookupEq (HeapEntry hp)
             f Base      = return mempty
             f t = error $ "reduceEq: fetch: " ++ show (t,i,vals)
         {-# SCC "Fetch.mappend" #-} liftM mconcat $ mapM f vals
reduceEq (Apply a b)
    = do Rhs vals <- lookupEq (VarEntry a)
         let f (Tag func FunctionNode 1 args)
                 = reduceEq (Ident func)
             f (Tag conc nt n args)
                 | n == 0    = return mempty
                 | otherwise = do bRhs <- lookupEq (VarEntry b)
                                  return $ singleton (Tag conc nt (n-1) (args ++ [bRhs]))
             f t             = error $ "reduceEq: apply: " ++ show t
         {-# SCC "Apply.mappend" #-} liftM mconcat $ mapM f vals
reduceEq (PartialApply a b)
    = do Rhs vals <- lookupEq (VarEntry a)
         let f (Tag tag nt n args)
                 | n == 0    = return mempty
                 | otherwise = do bRhs <- lookupEq (VarEntry b)
                                  return $ singleton (Tag tag nt (n-1) (args ++ [bRhs]))
             f t             = error $ "reduceEq: apply: " ++ show t
         {-# SCC "PartialApply.mappend" #-} liftM mconcat $ mapM f vals
reduceEq (Update hp val)
    = do Rhs hps <- lookupEq (VarEntry hp)
         valRhs  <- lookupEq (VarEntry val)
         forM_ hps $ \(Heap hp) -> addReduced (HeapEntry hp) valRhs
         return mempty

-- FIXME: Throw an exception if 'lhs' couldn't be found.
lookupEq :: Lhs -> M Rhs
lookupEq lhs
    = asks $ \(eqs, _sharingMap) -> Map.findWithDefault mempty lhs eqs

-- FIXME: Throw an exception if 'lhs' couldn't be found.
isShared :: Lhs -> M Bool
isShared lhs
    = asks $ \(_eqs, sharingMap) -> Map.findWithDefault False lhs sharingMap

setShared :: HeapPointer -> M ()
setShared hp = tell (mempty, Endo $ Map.insert (HeapEntry hp) True)