From 178af1ca24177aa9edcb319b3449b9aa0e838897 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Fri, 25 Jan 2008 16:17:17 +0000 Subject: [PATCH] Changed the array usage checking to distinguish between read-from and written-to indexes, while also beginning to overhaul the system to make a more general usage-checking framework that operates on the control flow graph --- Makefile.am | 2 +- common/PassList.hs | 2 +- transformations/ArrayUsageCheck.hs | 154 ++++++++++++++++--------- transformations/ArrayUsageCheckTest.hs | 4 +- transformations/UsageCheck.hs | 54 +++++++++ 5 files changed, 158 insertions(+), 58 deletions(-) create mode 100644 transformations/UsageCheck.hs diff --git a/Makefile.am b/Makefile.am index 929e322..7cbad0f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -54,7 +54,7 @@ CLEANFILES = $(BUILT_SOURCES) tock_SOURCES_hs = transformations/SimplifyExprs.hs transformations/SimplifyTypes.hs tock_SOURCES_hs += transformations/Unnest.hs transformations/RainUsageCheck.hs transformations/SimplifyProcs.hs tock_SOURCES_hs += transformations/SimplifyComms.hs transformations/ArrayUsageCheck.hs -tock_SOURCES_hs += transformations/Omega.hs +tock_SOURCES_hs += transformations/Omega.hs transformations/UsageCheck.hs tock_SOURCES_hs += frontends/PreprocessOccam.hs frontends/ParseRain.hs frontends/StructureOccam.hs tock_SOURCES_hs += frontends/ParseOccam.hs frontends/RainTypes.hs frontends/RainPasses.hs frontends/ParseUtils.hs tock_SOURCES_hs += common/Pass.hs common/TreeUtils.hs common/Intrinsics.hs common/EvalLiterals.hs diff --git a/common/PassList.hs b/common/PassList.hs index a4b2e6f..6d06a81 100644 --- a/common/PassList.hs +++ b/common/PassList.hs @@ -36,7 +36,7 @@ commonPasses opts = [ ("Simplify types", simplifyTypes) , ("Simplify expressions", simplifyExprs) ] - ++ (if csUsageChecking opts then [("Check array usage", checkArrayUsage)] else []) ++ + ++ (if csUsageChecking opts then [("Usage checks", usageCheckPass)] else []) ++ [ ("Simplify processes", simplifyProcs) , ("Flatten nested declarations", unnest) diff --git a/transformations/ArrayUsageCheck.hs b/transformations/ArrayUsageCheck.hs index f70337b..6225ad4 100644 --- a/transformations/ArrayUsageCheck.hs +++ b/transformations/ArrayUsageCheck.hs @@ -16,12 +16,12 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . -} -module ArrayUsageCheck (checkArrayUsage, FlattenedExp(..), makeEquations, makeReplicatedEquations, VarMap) where +module ArrayUsageCheck (checkArrayUsage, FlattenedExp(..), makeEquations, makeReplicatedEquations, usageCheckPass, VarMap) where import Control.Monad.Error import Control.Monad.State import Data.Array.IArray -import Data.Generics hiding (GT) +import Data.Graph.Inductive import Data.List import qualified Data.Map as Map import Data.Maybe @@ -35,25 +35,52 @@ import Metadata import Omega import Pass import Types +import UsageCheck import Utils +usageCheckPass :: Pass +usageCheckPass t = do g' <- buildFlowGraph labelFunctions t + g <- case g' of + Left err -> die err + Right g -> return g + checkArrayUsage g undefined -- TODO do we need a start node? + return t + + -- TODO we should probably calculate this from the CFG -checkArrayUsage :: Data a => a -> PassM a -checkArrayUsage tree = (mapM_ checkPar $ listify (const True) tree) >> return tree +checkArrayUsage :: forall m. (Die m, CSM m) => FlowGraph m (Maybe Decl, Vars) -> Node -> m () +checkArrayUsage graph startNode = sequence_ $ checkPar checkArrayUsage' graph startNode where - -- TODO this doesn't actually check that the uses are in parallel; - -- they might be in sequence within the parallel! - checkPar :: A.Process -> PassM () - checkPar (A.Par m _ p) = mapM_ (checkIndexes m) $ Map.toList $ Map.fromListWith (++) $ mapMaybe groupArrayIndexes $ listify (const True) p - checkPar _ = return () + -- TODO take proper account of replication! + flatten :: ParItems a -> [a] + flatten (ParItem x) = [x] + flatten (ParItems xs) = concatMap flatten xs + flatten (RepParItem _ x) = flatten x --TODO + + checkArrayUsage' :: (Meta, ParItems (Maybe Decl, Vars)) -> m () + checkArrayUsage' (m,p) = mapM_ (checkIndexes m) $ Map.toList $ + foldl (Map.unionWith (\(a,b) (c,d) -> (a ++ c, b ++ d))) Map.empty $ map (groupArrayIndexes . snd) $ flatten p - groupArrayIndexes :: A.Variable -> Maybe (String,[A.Expression]) - -- TODO this is quite hacky: - groupArrayIndexes (A.SubscriptedVariable _ (A.Subscript _ e) (A.Variable _ n)) - = Just (A.nameName n, [e]) - groupArrayIndexes _ = Nothing + -- Returns (array name, list of written-to indexes, list of read-from indexes) + groupArrayIndexes :: Vars -> Map.Map String ([A.Expression], [A.Expression]) + groupArrayIndexes vs = zipMap join (makeList (writtenVars vs)) (makeList (readVars vs)) + where + join :: Maybe [a] -> Maybe [a] -> Maybe ([a],[a]) + join x y = Just (maybe [] id x, maybe [] id y) + + makeList :: Set.Set Var -> Map.Map String [A.Expression] + makeList = Set.fold (maybe id (uncurry $ Map.insertWith (++)) . getArrayIndex) Map.empty + +-- sortAndGroupBy :: (a -> a -> Ordering) -> [a] -> [[a]] +-- sortAndGroupBy f = groupBy ((== EQ) . f) . sortBy f + + -- TODO this is quite hacky: + getArrayIndex :: Var -> Maybe (String, [A.Expression]) + getArrayIndex (Var (A.SubscriptedVariable _ (A.Subscript _ e) (A.Variable _ n))) + = Just (A.nameName n, [e]) + getArrayIndex _ = Nothing - checkIndexes :: Meta -> (String,[A.Expression]) -> PassM () + checkIndexes :: Meta -> (String,([A.Expression],[A.Expression])) -> m () checkIndexes m (arrName, indexes) = do userArrName <- getRealName (A.Name undefined undefined arrName) arrType <- typeOfName (A.Name undefined undefined arrName) @@ -73,7 +100,7 @@ checkArrayUsage tree = (mapM_ checkPar $ listify (const True) tree) >> return tr ((varMapping,vm):_) -> do sol <- formatSolution varMapping (getCounterEqs vm) dieP m $ "Overlapping indexes of array \"" ++ userArrName ++ "\" when: " ++ sol - formatSolution :: VarMap -> Map.Map CoeffIndex Integer -> PassM String + formatSolution :: VarMap -> Map.Map CoeffIndex Integer -> m String formatSolution varToIndex indexToConst = do names <- mapM valOfVar $ Map.assocs varToIndex return $ concat $ intersperse " , " $ catMaybes names where @@ -83,10 +110,10 @@ checkArrayUsage tree = (mapM_ checkPar $ listify (const True) tree) >> return tr return $ Just $ varExp' ++ " = " ++ show val -- TODO this is surely defined elsewhere already? - getRealName :: A.Name -> PassM String + getRealName :: A.Name -> m String getRealName n = lookupName n >>* A.ndOrigName - showFlattenedExp :: FlattenedExp -> PassM String + showFlattenedExp :: FlattenedExp -> m String showFlattenedExp (Const n) = return $ show n showFlattenedExp (Scale n ((A.Variable _ vn),vi)) = do vn' <- getRealName vn >>* (\s -> if vi == 0 then s else s ++ replicate vi '\'' ) @@ -105,7 +132,7 @@ checkArrayUsage tree = (mapM_ checkPar $ listify (const True) tree) >> return tr bottom' <- showFlattenedExpSet bottom return $ "(" ++ top' ++ " / " ++ bottom' ++ ")" - showFlattenedExpSet :: Set.Set FlattenedExp -> PassM String + showFlattenedExpSet :: Set.Set FlattenedExp -> m String showFlattenedExpSet s = liftM concat $ sequence $ intersperse (return " + ") $ map showFlattenedExp $ Set.toList s -- | A type for inside makeEquations: @@ -148,10 +175,12 @@ onlyConst _ = Nothing -- Each item in the left branch can be paired with each other, and each item in the left branch can -- be paired with all other items. data ArrayAccess = - Single (EqualityConstraintEquation,EqualityProblem,InequalityProblem) - | Group [(EqualityConstraintEquation,EqualityProblem,InequalityProblem)] + Single (ArrayAccessType, (EqualityConstraintEquation, EqualityProblem, InequalityProblem)) + | Group [(ArrayAccessType, (EqualityConstraintEquation, EqualityProblem, InequalityProblem))] | Replicated [ArrayAccess] [ArrayAccess] +data ArrayAccessType = AAWrite | AARead + makeExpSet :: [FlattenedExp] -> Either String (Set.Set FlattenedExp) makeExpSet = foldM makeExpSet' Set.empty where @@ -185,8 +214,8 @@ makeExpSet = foldM makeExpSet' Set.empty type VarMap = Map.Map FlattenedExp Int --- | Given a list of (replicated variable, start, count), a list of parallel array accesses, the length of the array, --- returns the problems +-- | Given a list of (replicated variable, start, count), a list of (written,read) parallel array accesses, +-- the length of the array, returns the problems. -- -- The general strategy is as follows. -- For every array index (here termed an "access"), we transform it into @@ -205,25 +234,34 @@ type VarMap = Map.Map FlattenedExp Int -- -- The remainder of the work (correctly pairing equations) is done by -- squareAndPair. -makeReplicatedEquations :: [(A.Variable, A.Expression, A.Expression)] -> [A.Expression] -> A.Expression -> +makeReplicatedEquations :: [(A.Variable, A.Expression, A.Expression)] -> ([A.Expression],[A.Expression]) -> A.Expression -> Either String [(VarMap, (EqualityProblem, InequalityProblem))] makeReplicatedEquations repVars accesses bound - = do flattenedAccesses <- mapM flatten accesses - let flattenedAccessesMirror = concatMap (\(v,_,_) -> map (setIndexVar v 1) flattenedAccesses) repVars + = do flattenedAccesses <- applyPairM (mapM flatten) accesses + let flattenedAccessesMirror = applyPair concat $ unzip $ map (\(v,_,_) -> applyPair (map (setIndexVar v 1)) flattenedAccesses) repVars bound' <- flatten bound ((v,h,repVars',repVarIndexes),s) <- (flip runStateT) Map.empty $ do repVars' <- mapM (\(v,s,c) -> - do s' <- lift (flatten s) >>= makeEquation >>= getSingleAccessItem "Modulo or Divide not allowed in replication start" - c' <- lift (flatten c) >>= makeEquation >>= getSingleAccessItem "Modulo or Divide not allowed in replication count" + do s' <- lift (flatten s) >>= makeEquation (error "Type is irrelevant for replication count") + >>= getSingleAccessItem "Modulo or Divide not allowed in replication start" + c' <- lift (flatten c) >>= makeEquation (error "Type is irrelevant for replication count") + >>= getSingleAccessItem "Modulo or Divide not allowed in replication count" return (v,s',c')) repVars - accesses' <- mapM (makeEquationWithPossibleRepBounds repVars' <.< makeEquation) flattenedAccesses - accesses'' <- mapM (makeEquationWithPossibleRepBounds repVars' <.< makeEquation) flattenedAccessesMirror - high <- makeEquation bound' >>= getSingleAccessItem "Multiple possible upper bounds not supported" + accesses' <- mapM (makeEquationWithPossibleRepBounds repVars') =<< makeEquationsWR flattenedAccesses + accesses'' <- mapM (makeEquationWithPossibleRepBounds repVars') =<< makeEquationsWR flattenedAccessesMirror + high <- makeEquation (error "Type is irrelevant for uppper bound") bound' + >>= getSingleAccessItem "Multiple possible upper bounds not supported" repVarIndexes <- mapM (\(v,_,_) -> seqPair (varIndex (Scale 1 (v,0)), varIndex (Scale 1 (v,1)))) repVars return (Replicated accesses' accesses'',high, repVars',repVarIndexes) return $ squareAndPair repVarIndexes s [v] (amap (const 0) h, addConstant (-1) h) where + makeEquationsWR :: ([[FlattenedExp]],[[FlattenedExp]]) -> StateT (VarMap) (Either String) [ArrayAccess] + makeEquationsWR (w,r) = do w' <- mapM (makeEquation AAWrite) w + r' <- mapM (makeEquation AARead) r + return (w' ++ r') + + setIndexVar :: A.Variable -> Int -> [FlattenedExp] -> [FlattenedExp] setIndexVar tv ti es = case mapAccumL (setIndexVar' tv ti) False es of (_, es') -> es' @@ -246,12 +284,12 @@ makeReplicatedEquations repVars accesses bound addPossibleRepBound' :: ArrayAccess -> (A.Variable, Int, EqualityConstraintEquation, EqualityConstraintEquation) -> StateT (VarMap) (Either String) ArrayAccess - addPossibleRepBound' (Group accesses) v = mapM (flip addPossibleRepBound v) accesses >>* Group + addPossibleRepBound' (Group accesses) v = mapM (seqPair . transformPair return (flip addPossibleRepBound v)) accesses >>* Group addPossibleRepBound' (Replicated acc0 acc1) v = do acc0' <- mapM (flip addPossibleRepBound' v) acc0 acc1' <- mapM (flip addPossibleRepBound' v) acc1 return $ Replicated acc0' acc1' - addPossibleRepBound' (Single acc) v = addPossibleRepBound acc v >>* Single + addPossibleRepBound' (Single (t,acc)) v = addPossibleRepBound acc v >>* (\x -> Single (t,x)) addPossibleRepBound :: (EqualityConstraintEquation, EqualityProblem, InequalityProblem) -> (A.Variable, Int, EqualityConstraintEquation, EqualityConstraintEquation) -> @@ -289,7 +327,7 @@ flatten (A.Dyadic m op lhs rhs) | op == A.Add = combine' (flatten lhs) (flatte | otherwise = throwError ("Unhandleable operator found in expression: " ++ show op) where -- liftM2L :: (Ord a, Ord b, Monad m) => (Set.Set a -> Set.Set b -> c) -> m [a] -> m [b] -> m [c] - liftM2L f x y = liftM (:[]) $ liftM2 f (x >>= makeExpSet) (y >>= makeExpSet) + liftM2L f x y = liftM singleton $ liftM2 f (x >>= makeExpSet) (y >>= makeExpSet) --TODO we need to handle lots more different expression types in future. @@ -392,42 +430,48 @@ squareAndPair repVars s v lh -- prime >= plain + 1 (prime - plain - 1 >= 0) extraIneq = [simpleArray [(prime,1), (plain,-1), (0, -1)]] +{- getSingles :: String -> [ArrayAccess] -> Either String [(EqualityConstraintEquation, EqualityProblem, InequalityProblem)] getSingles err = mapM getSingle where getSingle (Single acc) = return acc getSingle _ = throwError err +-} getSingleAccessItem :: MonadTrans m => String -> ArrayAccess -> m (Either String) EqualityConstraintEquation -getSingleAccessItem _ (Single (acc,_,_)) = lift $ return acc +getSingleAccessItem _ (Single (_,(acc,_,_))) = lift $ return acc getSingleAccessItem err _ = lift $ throwError err +{- getSingleAccess :: MonadTrans m => String -> ArrayAccess -> m (Either String) (EqualityConstraintEquation, EqualityProblem, InequalityProblem) getSingleAccess _ (Single acc) = lift $ return acc getSingleAccess err _ = lift $ throwError err +-} -- | Odd helper function for getting/asserting the first item of a triple from a singleton list inside a monad transformer (!) getSingleItem :: MonadTrans m => String -> [(a,b,c)] -> m (Either String) a getSingleItem _ [(item,_,_)] = lift $ return item getSingleItem err _ = lift $ throwError err --- | Given a list of expressions, an expression representing the upper array bound, returns either an error + +-- | Given a list of (written,read) expressions, an expression representing the upper array bound, returns either an error -- (because the expressions can't be handled, typically) or a set of equalities, inequalities and mapping from -- (unique, munged) variable name to variable-index in the equations. -- TODO probably want to take this into the PassM monad at some point, to use the Meta in the error message -- TODO allow "background knowledge" in the form of other equalities and inequalities -makeEquations :: [A.Expression] -> A.Expression -> Either String [(VarMap, (EqualityProblem, InequalityProblem))] -makeEquations es high = makeEquations' >>* uncurry3 (squareAndPair []) +makeEquations :: ([A.Expression],[A.Expression]) -> A.Expression -> Either String [(VarMap, (EqualityProblem, InequalityProblem))] +makeEquations (esW,esR) high = makeEquations' >>* uncurry3 (squareAndPair []) where -- | The body of makeEquations; returns the variable mapping, the list of (nx,ex) pairs and a pair -- representing the upper and lower bounds of the array (inclusive). makeEquations' :: Either String (VarMap, [ArrayAccess], (EqualityConstraintEquation, EqualityConstraintEquation)) makeEquations' = do ((v,h),s) <- (flip runStateT) Map.empty $ - do flattened <- lift (mapM flatten es) - eqs <- mapM makeEquation flattened - high' <- (lift $ flatten high) >>= makeEquation >>= getSingleAccessItem "Multiple possible upper bounds not supported" - return (eqs,high') + do eqsW <- mapM (makeEquation AAWrite) =<< lift (mapM flatten esW) + eqsR <- mapM (makeEquation AARead) =<< lift (mapM flatten esR) + high' <- (lift $ flatten high) >>= makeEquation (error "Type irrelevant for upper bound") + >>= getSingleAccessItem "Multiple possible upper bounds not supported" + return (eqsW ++ eqsR,high') return (s,v,(amap (const 0) h, addConstant (-1) h)) @@ -459,10 +503,10 @@ pairEqsAndBounds items bounds = (concatMap (uncurry pairEqs) . allPairs) items + pairEqs :: ArrayAccess -> ArrayAccess -> [(EqualityProblem, InequalityProblem)] - pairEqs (Single acc) (Single acc') = [pairEqs' acc acc'] - pairEqs (Single acc) (Group accs) = map (pairEqs' acc) accs - pairEqs (Group accs) (Single acc) = map (pairEqs' acc) accs - pairEqs (Group accs) (Group accs') = map (uncurry pairEqs') $ product2 (accs,accs') + pairEqs (Single acc) (Single acc') = maybeToList $ pairEqs' acc acc' + pairEqs (Single acc) (Group accs) = mapMaybe (pairEqs' acc) accs + pairEqs (Group accs) (Single acc) = mapMaybe (pairEqs' acc) accs + pairEqs (Group accs) (Group accs') = mapMaybe (uncurry pairEqs') $ product2 (accs,accs') pairEqs (Replicated rA rB) acc = concatMap (pairEqs acc) rA pairEqs acc (Replicated rA rB) @@ -473,10 +517,11 @@ pairEqsAndBounds items bounds = (concatMap (uncurry pairEqs) . allPairs) items + pairRep (Replicated rA rB) = (concatMap (uncurry pairEqs) $ product2 (rA,rB)) ++ concatMap (uncurry pairEqs) (allPairs rA) pairRep _ = [] - pairEqs' :: (EqualityConstraintEquation, EqualityProblem, InequalityProblem) - -> (EqualityConstraintEquation, EqualityProblem, InequalityProblem) - -> (EqualityProblem, InequalityProblem) - pairEqs' (ex,eqX,ineqX) (ey,eqY,ineqY) = ([arrayZipWith' 0 (-) ex ey] ++ eqX ++ eqY, ineqX ++ ineqY ++ getIneqs bounds [ex,ey]) + pairEqs' :: (ArrayAccessType,(EqualityConstraintEquation, EqualityProblem, InequalityProblem)) + -> (ArrayAccessType,(EqualityConstraintEquation, EqualityProblem, InequalityProblem)) + -> Maybe (EqualityProblem, InequalityProblem) + pairEqs' (AARead,_) (AARead,_) = Nothing + pairEqs' (_,(ex,eqX,ineqX)) (_,(ey,eqY,ineqY)) = Just ([arrayZipWith' 0 (-) ex ey] ++ eqX ++ eqY, ineqX ++ ineqY ++ getIneqs bounds [ex,ey]) -- | Given a (low,high) bound (typically: array dimensions), and a list of equations ex, -- forms the possible inequalities: @@ -494,14 +539,15 @@ getIneqs (low, high) = concatMap getLH addEq = arrayZipWith' 0 (+) -- | Given an expression, forms equations (and accompanying additional equation-sets) and returns it -makeEquation :: [FlattenedExp] -> StateT (VarMap) (Either String) ArrayAccess -makeEquation summedItems +makeEquation :: ArrayAccessType -> [FlattenedExp] -> StateT VarMap (Either String) ArrayAccess +makeEquation t summedItems = do eqs <- process summedItems let eqs' = map (transformTriple mapToArray (map mapToArray) (map mapToArray)) eqs return $ case eqs' of - [acc] -> Single acc - _ -> Group eqs' + [acc] -> Single (t,acc) + _ -> Group $ zip (repeat t) eqs' where + process :: [FlattenedExp] -> StateT VarMap (Either String) [(Map.Map Int Integer,[Map.Map Int Integer], [Map.Map Int Integer])] process = foldM makeEquation' empty makeEquation' :: [(Map.Map Int Integer,[Map.Map Int Integer], [Map.Map Int Integer])] -> FlattenedExp -> StateT (VarMap) (Either String) [(Map.Map Int Integer,[Map.Map Int Integer], [Map.Map Int Integer])] diff --git a/transformations/ArrayUsageCheckTest.hs b/transformations/ArrayUsageCheckTest.hs index 607208a..08fa9b6 100644 --- a/transformations/ArrayUsageCheckTest.hs +++ b/transformations/ArrayUsageCheckTest.hs @@ -361,13 +361,13 @@ testMakeEquations = TestList test :: (Integer,[(VarMap,[HandyEq],[HandyIneq])],[A.Expression],A.Expression) -> Test test (ind, problems, exprs, upperBound) = TestCase $ assertEquivalentProblems ("testMakeEquations " ++ show ind) - (map (transformPair id (uncurry makeConsistent)) $ map pairLatterTwo problems) =<< (checkRight $ makeEquations exprs upperBound) + (map (transformPair id (uncurry makeConsistent)) $ map pairLatterTwo problems) =<< (checkRight $ makeEquations (exprs,[]) upperBound) testRep :: (Integer,[(VarMap,[HandyEq],[HandyIneq])],[(A.Variable, A.Expression, A.Expression)],[A.Expression],A.Expression) -> Test testRep (ind, problems, reps, exprs, upperBound) = TestCase $ assertEquivalentProblems ("testMakeEquations " ++ show ind) (map (transformPair id (uncurry makeConsistent)) $ map pairLatterTwo problems) - =<< (checkRight $ makeReplicatedEquations reps exprs upperBound) + =<< (checkRight $ makeReplicatedEquations reps (exprs,[]) upperBound) pairLatterTwo (a,b,c) = (a,(b,c)) diff --git a/transformations/UsageCheck.hs b/transformations/UsageCheck.hs new file mode 100644 index 0000000..1f93f12 --- /dev/null +++ b/transformations/UsageCheck.hs @@ -0,0 +1,54 @@ +{- +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 . +-} + +module UsageCheck where + +import Data.Graph.Inductive +import qualified Data.Set as Set + +import qualified AST as A +import Errors +import FlowGraph +import Metadata + + +newtype Var = Var A.Variable + +data Vars = Vars { + readVars :: Set.Set Var + ,writtenVars :: Set.Set Var + ,usedVars :: Set.Set Var -- for channels, barriers, etc +} + +data Decl = ScopeIn String | ScopeOut String deriving (Show, Eq) + +data ParItems a + = ParItem a + | ParItems [ParItems a] + | RepParItem A.Replicator (ParItems a) + +-- | Given a function to check a list of graph labels, a flow graph +-- and a starting node, returns a list of monadic actions (slightly +-- more flexible than a monadic action giving a list) that will check +-- all PAR items in the flow graph +checkPar :: Monad m => ((Meta, ParItems a) -> m b) -> FlowGraph m a -> Node -> [m b] +checkPar = undefined -- TODO +--TODO is a start node actually necessary for checkPar? + +labelFunctions :: Die m => GraphLabelFuncs m (Maybe Decl, Vars) +labelFunctions = undefined -- TODO