
This makes sure that we catch all leftover instances of using SYB to do generic operations that we should be using Polyplate for instead. Most modules should only import Data, and possibly Typeable.
349 lines
14 KiB
Haskell
349 lines
14 KiB
Haskell
{-
|
|
Tock: a compiler for parallel languages
|
|
Copyright (C) 2007 University of Kent
|
|
|
|
This program is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License as published by the
|
|
Free Software Foundation, either version 2 of the License, or (at your
|
|
option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
-}
|
|
|
|
module ImplicitMobility (implicitMobility, mobiliseArrays, inferDeref) where
|
|
|
|
import Control.Monad
|
|
import Control.Monad.Trans
|
|
import Data.Generics (Data)
|
|
import Data.Graph.Inductive
|
|
import Data.Graph.Inductive.Query.DFS
|
|
import qualified Data.Map as Map
|
|
import Data.Maybe
|
|
import qualified Data.Set as Set
|
|
import qualified Data.Traversable as T
|
|
|
|
import qualified AST as A
|
|
import CompState
|
|
import Data.Generics.Polyplate.Route
|
|
import Errors
|
|
import FlowAlgorithms
|
|
import FlowGraph
|
|
import FlowUtils
|
|
import Intrinsics
|
|
import Metadata
|
|
import Pass
|
|
import ShowCode
|
|
import Traversal
|
|
import Types
|
|
import UsageCheckUtils
|
|
import Utils
|
|
|
|
effectDecision :: Var -> Decision -> AlterAST PassM () -> A.AST -> PassM A.AST
|
|
effectDecision _ Move _ = return -- Move is the default
|
|
effectDecision targetVar (Copy _) (AlterProcess wrapper) = routeModify wrapper alterProc
|
|
where
|
|
derefExp :: A.Expression -> PassM A.Expression
|
|
derefExp e
|
|
= do t <- astTypeOf e
|
|
{-case t of
|
|
A.Mobile (A.List _) -> return ()
|
|
A.List _ -> return ()
|
|
_ -> dieP (findMeta e) $
|
|
"Cannot dereference a non-list assignment RHS: " ++ show t -}
|
|
case e of
|
|
A.ExprVariable m' v ->
|
|
if (Var v == targetVar)
|
|
then return $ A.CloneMobile m' $ A.ExprVariable m' v
|
|
else return e
|
|
-- TODO handle concat expressions with repeated vars
|
|
{-
|
|
A.Dyadic m A.Concat lhs rhs ->
|
|
do lhs' <- derefExp lhs
|
|
rhs' <- derefExp rhs
|
|
return $ A.Dyadic m A.Concat lhs' rhs'
|
|
-}
|
|
_ -> return e
|
|
alterProc :: A.Process -> PassM A.Process
|
|
alterProc (A.Assign m lhs (A.ExpressionList m' [e]))
|
|
= return $ A.Assign m lhs $ A.ExpressionList m' [A.CloneMobile m' e]
|
|
alterProc (A.Output m cv [A.OutExpression m' e])
|
|
= return $ A.Output m cv [A.OutExpression m' $ A.CloneMobile m' e]
|
|
alterProc x = dieP (findMeta x) "Cannot alter process to copy"
|
|
effectDecision _ (Copy _) _ = return
|
|
|
|
-- | Calculates a map from each node to a set of variables that will be
|
|
-- used again afterwards. Used in this context means it can possibly be
|
|
-- read from before being written to
|
|
calculateUsedAgainAfter :: Monad m => FlowGraph m UsageLabel -> Node -> Either String (Map.Map Node
|
|
(Set.Set Var))
|
|
calculateUsedAgainAfter g startNode
|
|
= flowAlgorithm funcs (rdfs [startNode] g) (startNode, Set.empty)
|
|
where
|
|
funcs :: GraphFuncs Node EdgeLabel (Set.Set Var)
|
|
funcs = GF
|
|
{ nodeFunc = iterate
|
|
-- Backwards data flow:
|
|
, nodesToProcess = lsuc g
|
|
, nodesToReAdd = lpre g
|
|
, defVal = Set.empty
|
|
, userErrLabel = ("for node at: " ++) . show . fmap getNodeMeta . lab g
|
|
}
|
|
|
|
iterate :: (Node, EdgeLabel) -> Set.Set Var -> Maybe (Set.Set Var) -> Set.Set
|
|
Var
|
|
iterate node prevVars maybeVars = case lab g (fst node) of
|
|
Just ul ->
|
|
let vs = nodeVars $ getNodeData ul
|
|
readFromVars = readVars vs
|
|
writtenToVars = writtenVars vs
|
|
addTo = fromMaybe prevVars maybeVars
|
|
in (readFromVars `Set.union` addTo) `Set.difference` Map.keysSet writtenToVars
|
|
Nothing -> error "Node label not found in calculateUsedAgainAfter"
|
|
|
|
|
|
--TODO rememember to take note of declarations/scope, otherwise this:
|
|
-- seqeach (..) {int:x; ... x = 3;}
|
|
-- will look like x is used again on the next loop iteration
|
|
|
|
-- TODO look at the types, too!
|
|
printMoveCopyDecisions :: Decisions -> PassM ()
|
|
printMoveCopyDecisions decs
|
|
= mapM_ printDec $ Map.toList decs
|
|
where
|
|
printDec :: ((Node, Var), Decision) -> PassM ()
|
|
printDec ((_,v), dec) = astTypeOf v >>= \t -> (liftIO $ putStrLn $
|
|
show (findMeta v) ++ show v ++ " " ++ show t ++ " " ++ show dec)
|
|
|
|
data Decision = Move | Copy Meta deriving (Show, Ord, Eq)
|
|
|
|
makeMoveCopyDecisions :: forall m. Monad m => FlowGraph m UsageLabel -> [Node] ->
|
|
PassM Decisions
|
|
makeMoveCopyDecisions grOrig ns
|
|
= do namesWithTypes <- getCompState >>* csNames >>= T.mapM (typeOfSpec . A.ndSpecType)
|
|
let mobVars = Set.mapMonotonic (Var . A.Variable emptyMeta . A.Name emptyMeta)
|
|
. Map.keysSet
|
|
. Map.filter isJustMobileType
|
|
$ namesWithTypes
|
|
foldM (processConnected $ nmap (fmap $ filterVars mobVars) grOrig) (Map.empty) ns
|
|
where
|
|
isJustMobileType :: Maybe A.Type -> Bool
|
|
isJustMobileType (Just (A.Mobile {})) = True
|
|
isJustMobileType _ = False
|
|
|
|
filterVars :: Set.Set Var -> UsageLabel -> UsageLabel
|
|
filterVars keep u
|
|
= u { nodeVars = filterNodeVars (nodeVars u) }
|
|
where
|
|
keepM = Map.fromAscList $ flip zip (repeat ()) $ Set.toAscList keep
|
|
|
|
filterNodeVars :: Vars -> Vars
|
|
filterNodeVars vs
|
|
= vs { readVars = readVars vs `Set.intersection` keep
|
|
, writtenVars = writtenVars vs `Map.intersection` keepM
|
|
, usedVars = readVars vs `Set.intersection` keep }
|
|
|
|
-- Processes the entire sub-graph that is connected to the given node
|
|
processConnected :: FlowGraph m UsageLabel -> Map.Map (Node, Var) Decision -> Node ->
|
|
PassM (Map.Map (Node, Var) Decision)
|
|
processConnected gr m n = case calculateUsedAgainAfter gr n of
|
|
Left err -> dieP (getNodeMeta $ fromJust $ lab gr n) err
|
|
Right mvs -> foldM (processNode gr mvs) m $ Map.keys mvs
|
|
|
|
-- Processes all the variables at a given node
|
|
processNode :: FlowGraph m UsageLabel -> Map.Map Node (Set.Set Var) -> Map.Map (Node, Var) Decision
|
|
-> Node -> PassM (Map.Map (Node, Var) Decision)
|
|
processNode gr mvs m n
|
|
= case fmap (readVars . nodeVars . getNodeData) $ lab gr n of
|
|
Nothing -> dieP emptyMeta "Did not find node label during implicit mobility"
|
|
Just rvs -> return $ foldl (process n mvs) m $ Set.toList rvs
|
|
|
|
-- Processes a single variable at a given node
|
|
process :: Node -> Map.Map Node (Set.Set Var) -> Map.Map (Node, Var) Decision ->
|
|
Var -> Map.Map (Node, Var) Decision
|
|
process n useAgain prev v = let s = Map.findWithDefault Set.empty n useAgain
|
|
in Map.insert (n, v)
|
|
(if v `Set.member` s
|
|
then Copy $ findMeta $ Set.findMin $ Set.filter (== v) s
|
|
else Move) prev
|
|
|
|
type Decisions = Map.Map (Node, Var) Decision
|
|
|
|
effectMoveCopyDecisions :: FlowGraph PassM UsageLabel -> Decisions -> A.AST -> PassM A.AST
|
|
effectMoveCopyDecisions g decs = foldFuncsM $ map effect $ Map.toList decs
|
|
where
|
|
effect :: ((Node, Var), Decision) -> A.AST -> PassM A.AST
|
|
effect ((n, v), dec)
|
|
= case fmap getNodeFunc $ lab g n of
|
|
Nothing -> const $ dieP (findMeta v) "Could not find label for node"
|
|
Just mod -> effectDecision v dec mod
|
|
|
|
implicitMobility :: Pass A.AST
|
|
implicitMobility
|
|
= pass "Implicit mobility optimisation"
|
|
[] [] --TODO properties
|
|
(passOnlyOnAST "implicitMobility" $ \t -> do
|
|
g' <- buildFlowGraph labelUsageFunctions t
|
|
:: PassM (Either String (FlowGraph' PassM UsageLabel (), [Node],
|
|
[Node]))
|
|
case g' of
|
|
Left err -> dieP emptyMeta $ "Error building flow graph: " ++ err
|
|
Right (g, roots, terms) ->
|
|
-- We go from the terminator nodes, because we are performing backward
|
|
-- data-flow analysis
|
|
do decs <- makeMoveCopyDecisions g terms
|
|
printMoveCopyDecisions decs
|
|
effectMoveCopyDecisions g decs t)
|
|
|
|
mobiliseArrays :: PassASTOnStruct
|
|
mobiliseArrays = pass "Make all arrays mobile" [] [] recurse
|
|
where
|
|
ops :: ExtOpMSP BaseOp
|
|
ops = baseOp `extOpMS` (ops, doStructured)
|
|
|
|
recurse :: RecurseM PassM (ExtOpMSP BaseOp)
|
|
recurse = makeRecurseM ops
|
|
descend :: DescendM PassM (ExtOpMSP BaseOp)
|
|
descend = makeDescendM ops
|
|
|
|
doStructured :: TransformStructured' (ExtOpMSP BaseOp)
|
|
doStructured s@(A.Spec m (A.Specification m' n (A.Declaration m'' t@(A.Array ds
|
|
innerT))) scope)
|
|
= case innerT of
|
|
A.Chan {} -> case mobiliseArrayInside (t, A.Declaration m'') of
|
|
Just newSpec ->
|
|
do modifyName n (\nd -> nd {A.ndSpecType = newSpec})
|
|
recurse scope >>* A.Spec m (A.Specification m' n newSpec)
|
|
Nothing -> descend s
|
|
A.ChanEnd {} -> case mobiliseArrayInside (t, A.Declaration m'') of
|
|
Just newSpec ->
|
|
do modifyName n (\nd -> nd {A.ndSpecType = newSpec})
|
|
recurse scope >>* A.Spec m (A.Specification m' n newSpec)
|
|
Nothing -> descend s
|
|
_ -> do scope' <- recurse {-addAtEndOfScopeDyn m'' (A.ClearMobile m'' $ A.Variable m' n)-} scope
|
|
let newSpec = A.Is m'' A.Original (A.Mobile t) $
|
|
A.ActualExpression $ A.AllocMobile m'' (A.Mobile t) Nothing
|
|
modifyName n (\nd -> nd {A.ndSpecType = newSpec})
|
|
return $ A.Spec m (A.Specification m' n newSpec) scope'
|
|
|
|
doStructured (A.Spec m (A.Specification m' n (A.Proc m'' sm fs body)) scope)
|
|
= do scope' <- recurse scope
|
|
body' <- recurse body
|
|
fs' <- mapM processFormal fs
|
|
let newSpecF = A.Proc m'' sm fs'
|
|
modifyName n (\nd -> nd {A.ndSpecType =
|
|
let A.Proc _ _ _ stub = A.ndSpecType nd in newSpecF stub})
|
|
return $ A.Spec m (A.Specification m' n (newSpecF body')) scope'
|
|
|
|
doStructured (A.Spec m (A.Specification m' n (A.Protocol m'' ts)) scope)
|
|
= do let ts' = [case t of
|
|
A.Array {} -> A.Mobile t
|
|
_ -> t
|
|
| t <- ts]
|
|
newSpec = A.Protocol m'' ts'
|
|
modifyName n (\nd -> nd {A.ndSpecType = newSpec})
|
|
scope' <- recurse scope
|
|
return $ A.Spec m (A.Specification m' n newSpec) scope'
|
|
|
|
doStructured (A.Spec m (A.Specification m' n (A.ProtocolCase m'' nts)) scope)
|
|
= do let nts' = [(n, [case t of
|
|
A.Array {} -> A.Mobile t
|
|
_ -> t
|
|
| t <- ts]) | (n, ts) <- nts]
|
|
newSpec = A.ProtocolCase m'' nts'
|
|
modifyName n (\nd -> nd {A.ndSpecType = newSpec})
|
|
scope' <- recurse scope
|
|
return $ A.Spec m (A.Specification m' n newSpec) scope'
|
|
|
|
-- Must also mobilise channels of arrays, and arrays of channels of arrays:
|
|
doStructured s@(A.Spec m (A.Specification m' n st) scope)
|
|
= do mtf <- typeOfSpec' st
|
|
case mtf >>= mobiliseArrayInside of
|
|
Just newSpec ->
|
|
do scope' <- recurse scope
|
|
modifyName n (\nd -> nd {A.ndSpecType = newSpec})
|
|
return $ A.Spec m (A.Specification m' n newSpec) scope'
|
|
Nothing -> descend s
|
|
|
|
doStructured s = descend s
|
|
|
|
processFormal :: A.Formal -> PassM A.Formal
|
|
processFormal f@(A.Formal am t n)
|
|
= case mobiliseArrayInside (t, A.Declaration (A.nameMeta n)) of
|
|
Just decl@(A.Declaration _ t') ->
|
|
do modifyName n $ \nd -> nd {A.ndSpecType = decl}
|
|
return $ A.Formal am t' n
|
|
Nothing -> return f
|
|
|
|
mobiliseArrayInside :: (A.Type, A.Type -> A.SpecType) -> Maybe A.SpecType
|
|
mobiliseArrayInside (A.Chan attr t@(A.Array {}), f)
|
|
= Just $ f $ A.Chan attr $ A.Mobile t
|
|
mobiliseArrayInside (A.ChanEnd attr dir t@(A.Array {}), f)
|
|
= Just $ f $ A.ChanEnd attr dir $ A.Mobile t
|
|
mobiliseArrayInside (A.Array ds (A.Chan attr t@(A.Array {})), f)
|
|
= Just $ f $ A.Array ds $ A.Chan attr $ A.Mobile t
|
|
mobiliseArrayInside (A.Array ds (A.ChanEnd attr dir t@(A.Array {})), f)
|
|
= Just $ f $ A.Array ds $ A.ChanEnd attr dir $ A.Mobile t
|
|
mobiliseArrayInside _ = Nothing
|
|
|
|
class Dereferenceable a where
|
|
deref :: Meta -> a -> Maybe a
|
|
|
|
instance Dereferenceable A.Variable where
|
|
deref m = Just . A.DerefVariable m
|
|
|
|
instance Dereferenceable A.Expression where
|
|
deref m (A.ExprVariable m' v) = fmap (A.ExprVariable m') $ deref m v
|
|
deref m (A.AllocMobile _ _ (Just e)) = Just e
|
|
deref _ _ = Nothing
|
|
|
|
instance Dereferenceable A.Actual where
|
|
deref m (A.ActualVariable v) = fmap A.ActualVariable $ deref m v
|
|
deref m (A.ActualExpression e) = fmap A.ActualExpression $ deref m e
|
|
|
|
inferDeref :: PassOn2 A.Process A.Variable
|
|
inferDeref = pass "Infer mobile dereferences" [] [] recurse
|
|
where
|
|
ops = baseOp `extOpM` doProcess `extOpM` doVariable
|
|
|
|
recurse :: RecurseM PassM (TwoOpM PassM A.Process A.Variable)
|
|
recurse = makeRecurseM ops
|
|
descend :: DescendM PassM (TwoOpM PassM A.Process A.Variable)
|
|
descend = makeDescendM ops
|
|
|
|
unify :: (Dereferenceable a, ASTTypeable a, ShowOccam a, ShowRain a) => Meta
|
|
-> A.Type -> a -> PassM a
|
|
unify _ (A.Mobile t) x = return x
|
|
unify m t x = do xt <- astTypeOf x
|
|
case xt of
|
|
A.Mobile {} -> case deref m x of
|
|
Just x' -> return x'
|
|
Nothing -> diePC m $ formatCode "Unable to dereference %" x
|
|
_ -> return x
|
|
|
|
doProcess :: Transform A.Process
|
|
doProcess (A.ProcCall m n as)
|
|
= do A.Proc _ _ fs _ <- specTypeOfName n
|
|
ts <- mapM astTypeOf fs
|
|
as' <- mapM (uncurry $ unify m) (zip ts as)
|
|
return $ A.ProcCall m n as'
|
|
doProcess (A.IntrinsicProcCall m n as)
|
|
= do let Just amtns = lookup n intrinsicProcs
|
|
as' <- mapM (uncurry $ unify m) (zip (map mid amtns) as)
|
|
return $ A.IntrinsicProcCall m n as'
|
|
where mid (_,y,_) = y
|
|
doProcess p = descend p
|
|
|
|
doVariable :: Transform A.Variable
|
|
doVariable all@(A.SubscriptedVariable m sub v)
|
|
= do t <- astTypeOf v
|
|
case t of
|
|
A.Mobile {} -> return $ A.SubscriptedVariable m sub $ fromJust (deref m v)
|
|
_ -> descend all
|
|
doVariable v = descend v
|