Changed the checking for plain var usage so that if there is a problem in a replicated PAR, it checks for a solution to the replicators to see if that problem can actually ever occur

One remaining problem is that if the BK is different between the two plain var uses being checked, that is currently not dealt with correctly
This commit is contained in:
Neil Brown 2009-02-05 16:07:38 +00:00
parent 95d25c3dbc
commit 2120a294ed
2 changed files with 116 additions and 45 deletions

View File

@ -16,7 +16,20 @@ 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 ArrayUsageCheck (BackgroundKnowledge(..), BK, checkArrayUsage, FlattenedExp(..), makeEquations, makeExpSet, ModuloCase(..), onlyConst, showFlattenedExp, VarMap, canonicalise, fmapFlattenedExp) where
module ArrayUsageCheck (
BackgroundKnowledge(..),
BK,
canonicalise,
checkArrayUsage,
findRepSolutions,
FlattenedExp(..),
fmapFlattenedExp,
makeEquations,
makeExpSet,
ModuloCase(..),
onlyConst,
showFlattenedExp,
VarMap) where
import Control.Monad.Error
import Control.Monad.State
@ -45,6 +58,28 @@ import Utils
type BK = [Map.Map Var [BackgroundKnowledge]]
type BK' = [Map.Map Var (EqualityProblem, InequalityProblem)]
-- | Given a list of replicators, and some background knowledge,
-- checks if there are any solutions for a combination of the normal replicator
-- constraints, and the given background knowledge.
-- Returns Nothing if no solutions, a String with a counter-example if there are solutions
findRepSolutions :: (CSMR m, MonadIO m) => [(A.Name, A.Replicator)] -> BK -> m (Maybe String)
findRepSolutions reps bk = case makeEquations (addReps $ ParItems $ map (\x -> SeqItems [(bk, [x], [])]) $
[A.ExprVariable (A.nameMeta n) $ A.Variable (A.nameMeta n) n
| (n, _) <- reps]) maxInt of
Right problems -> do
probs <- concatMapM id [formatProblem vm prob | (_,vm,prob) <- problems]
case mapMaybe solve problems of
[] -> return Nothing -- No solutions, safe
xs -> liftM (Just . concat) $ mapM format xs
res -> error $ "Unexpected reachability result"
where
maxInt = makeConstant emptyMeta $ fromInteger $ toInteger (maxBound :: Int32)
format ((lx,ly),varMapping,vm,problem)
= formatSolution varMapping (getCounterEqs vm)
addReps = flip (foldl $ flip RepParItem) reps
-- | A check-pass that checks the given ParItems (usually generated from a control-flow graph)
-- for any overlapping array indices.
checkArrayUsage :: forall m. (Die m, CSMR m, MonadIO m) => (Meta, ParItems (BK, UsageLabel)) -> m ()

View File

@ -68,10 +68,8 @@ usageCheckPass t = do g' <- buildFlowGraph labelUsageFunctions t
Right c -> return c
let g' = labelMapWithNodeId (addBK reach cons g) g
checkPar (nodeRep . snd)
(joinCheckParFunctions
checkArrayUsage
(checkPlainVarUsage . transformPair id (fmap snd)))
g'
(joinCheckParFunctions checkArrayUsage checkPlainVarUsage)
g'
checkParAssignUsage g' t
checkProcCallArgsUsage g' t
-- mapM_ (checkInitVar (findMeta t) g) roots
@ -80,7 +78,7 @@ usageCheckPass t = do g' <- buildFlowGraph labelUsageFunctions t
addBK :: Map.Map Node (Map.Map Var (Set.Set (Maybe A.Expression))) ->
Map.Map Node [A.Expression] -> FlowGraph PassM UsageLabel ->
Node -> FNode PassM UsageLabel -> FNode PassM (BK, UsageLabel)
addBK mp mp2 g nid n = fmap ((,) $ (map Map.fromList $ productN $ conBK ++
addBK mp mp2 g nid n = fmap ((,) $ (map (Map.fromListWith (++)) $ productN $ conBK ++
repBK ++ values)) n
where
nodeInQuestion :: Map.Map Var (Set.Set (Maybe A.Expression))
@ -134,9 +132,9 @@ addBK mp mp2 g nid n = fmap ((,) $ (map Map.fromList $ productN $ conBK ++
bk = [ RepBoundsIncl v low (subOne $ A.Dyadic m A.Add low count)]
-- filter out replicators, leave everything else in:
filterPlain :: CSMR m => Set.Set Var -> m (Set.Set Var)
filterPlain vs = do defs <- getCompState >>* (Map.map A.ndSpecType . csNames)
return $ Set.filter (plain defs) vs
filterPlain :: CSMR m => m (Var -> Bool)
filterPlain = do defs <- getCompState >>* (Map.map A.ndSpecType . csNames)
return $ plain defs
where
plain defs (Var v) = all nonRep (listify (const True :: A.Variable -> Bool) v)
where
@ -147,7 +145,7 @@ filterPlain vs = do defs <- getCompState >>* (Map.map A.ndSpecType . csNames)
filterPlain' :: CSMR m => ExSet Var -> m (ExSet Var)
filterPlain' Everything = return Everything
filterPlain' (NormalSet s) = filterPlain s >>* NormalSet
filterPlain' (NormalSet s) = filterPlain >>* flip Set.filter s >>* NormalSet
-- | I am not sure how you could build this out of the standard functions, so I built it myself
--Takes a list (let's say Y), a function that applies to a single item and a list, and then goes through applying the function
@ -160,53 +158,90 @@ permuteHelper func (x:xs) = permuteHelper' func [] x xs
permuteHelper' func prev cur [] = [func cur prev]
permuteHelper' func prev cur (next:rest) = (func cur (prev ++ (next:rest))) : (permuteHelper' func (prev ++ [cur]) next rest)
checkPlainVarUsage :: forall m. (MonadIO m, Die m, CSMR m) => (Meta, ParItems UsageLabel) -> m ()
checkPlainVarUsage (m, p) = {- liftIO (putStrLn ("Checking: " ++ show (m,p))) >> -}
check p
data VarsBK = VarsBK {
readVarsBK :: Map.Map Var [BK]
,writtenVarsBK :: Map.Map Var ([A.Expression], [BK])
}
foldUnionVarsBK :: [VarsBK] -> VarsBK
foldUnionVarsBK = foldl join (VarsBK Map.empty Map.empty)
where
getVars :: ParItems UsageLabel -> Vars
getVars (SeqItems ss) = foldUnionVars $ map nodeVars ss
getVars (ParItems ps) = foldUnionVars $ map getVars ps
join (VarsBK r w) (VarsBK r' w')
= VarsBK (Map.unionWith (++) r r') (Map.unionWith (\(x,y) (x',y') -> (x++x',y++y')) w w')
checkPlainVarUsage :: forall m. (MonadIO m, Die m, CSMR m) => (Meta, ParItems (BK, UsageLabel)) -> m ()
checkPlainVarUsage (m, p) = check p
where
addBK :: BK -> Vars -> VarsBK
addBK bk vs = VarsBK (Map.fromAscList $ zip (Set.toAscList $ readVars vs) (repeat [bk]))
(Map.map (\me -> (maybeToList me, [bk])) $ writtenVars vs)
reps (RepParItem r p) = r : reps p
reps (SeqItems _) = []
reps (ParItems ps) = concatMap reps ps
getVars :: ParItems (BK, UsageLabel) -> VarsBK
getVars (SeqItems ss) = foldUnionVarsBK $ [addBK bk $ nodeVars u | (bk, u) <- ss]
getVars (ParItems ps) = foldUnionVarsBK $ map getVars ps
getVars (RepParItem _ p) = getVars p
getDecl :: ParItems UsageLabel -> [Var]
getDecl :: ParItems (BK, UsageLabel) -> [Var]
getDecl (ParItems ps) = concatMap getDecl ps
getDecl (RepParItem _ p) = getDecl p
getDecl (SeqItems ss) = mapMaybe
(fmap (Var . A.Variable emptyMeta . A.Name emptyMeta) . join . fmap getScopeIn . nodeDecl) ss
(fmap (Var . A.Variable emptyMeta . A.Name emptyMeta) . join . fmap getScopeIn . nodeDecl
. snd) ss
where
getScopeIn (ScopeIn _ n) = Just n
getScopeIn _ = Nothing
check :: ParItems UsageLabel -> m ()
-- Check does not have to descend, because the overall checkPlainVarUsage function
-- will be called on every single PAR in the whole tree
check :: ParItems (BK, UsageLabel) -> m ()
check (SeqItems {}) = return ()
check (ParItems ps) = sequence_ $ permuteHelper (checkCREW $ concatMap getDecl ps) (map getVars ps)
check (RepParItem _ p) = check (ParItems [p,p]) -- Easy way to check two replicated branches
checkCREW :: [Var] -> Vars -> [Vars] -> m ()
checkCREW :: [Var] -> VarsBK -> [VarsBK] -> m ()
checkCREW decl item rest
= do sharedNames <- getCompState >>* csNameAttr >>* Map.filter (== NameShared)
>>* Map.keysSet >>* (Set.map $ UsageCheckUtils.Var . A.Variable emptyMeta . A.Name emptyMeta)
writtenTwice <- filterPlain $
((Map.keysSet (writtenVars item)
`Set.intersection`
Map.keysSet (writtenVars otherVars)
) `Set.difference` Set.fromList decl
) `Set.difference` sharedNames
writtenAndRead <- filterPlain $
((Map.keysSet (writtenVars item)
`Set.intersection`
readVars otherVars
) `Set.difference` Set.fromList decl
) `Set.difference` sharedNames
when (not $ Set.null writtenTwice) $
diePC m $ formatCode
"The following variables are written-to in at least two places inside a PAR: %" writtenTwice
when (not $ Set.null writtenAndRead) $
diePC m $ formatCode
"The following variables are written-to and read-from in separate branches of a PAR: %" writtenAndRead
writtenTwice <- filterPlain >>* flip filterMapByKey
((writtenVarsBK item
`intersect`
writtenVarsBK otherVars
) `difference` (Set.fromList decl `Set.union` sharedNames)
)
writtenAndRead <- filterPlain >>* flip filterMapByKey
((writtenVarsBK item
`intersect`
readVarsBK otherVars
) `difference` (Set.fromList decl `Set.union` sharedNames)
)
checkBKReps
"The following variables are written-to in at least two places inside a PAR: % "
(Map.map (transformPair snd snd) writtenTwice)
checkBKReps
"The following variables are written-to and read-from in separate branches of a PAR: % "
(Map.map (transformPair snd id) writtenAndRead)
where
otherVars = foldUnionVars rest
intersect :: Ord k => Map.Map k v -> Map.Map k v' -> Map.Map k (v, v')
intersect = Map.intersectionWith (,)
difference m s = m `Map.difference` (Map.fromAscList $ zip (Set.toAscList
s) (repeat ()))
otherVars = foldUnionVarsBK rest
checkBKReps :: String -> Map.Map Var ([BK], [BK]) -> m ()
checkBKReps _ vs | Map.null vs = return ()
checkBKReps msg vs
= do sols <- if null (reps p)
-- If there are no replicators, it's definitely dangerous:
then return $ Map.map (const $ [Just ""]) $ vs
else mapMapM (mapM (findRepSolutions (reps p)) . map (uncurry (++)) . product2) vs
case Map.filter (not . null) $ Map.map catMaybes sols of
vs' | Map.null vs' -> return ()
| otherwise -> diePC m $ formatCode (msg ++ concat (concat
$ Map.elems vs')) (Map.keysSet vs')
showCodeExSet :: (CSMR m, Ord a, ShowOccam a, ShowRain a) => ExSet a -> m String
showCodeExSet Everything = return "<all-vars>"
@ -286,10 +321,10 @@ checkParAssignUsage g = mapM_ checkParAssign . findAllProcess isParAssign g
checkParAssign :: (A.Process, (BK, UsageLabel)) -> m ()
checkParAssign (A.Assign m vs _, (bk, _))
= do checkPlainVarUsage (m, mockedupParItems)
checkArrayUsage (m, fmap ((,) bk) mockedupParItems)
checkArrayUsage (m, mockedupParItems)
where
mockedupParItems :: ParItems UsageLabel
mockedupParItems = ParItems [SeqItems [Usage Nothing Nothing Nothing
mockedupParItems :: ParItems (BK, UsageLabel)
mockedupParItems = fmap ((,) bk) $ ParItems [SeqItems [Usage Nothing Nothing Nothing
$ processVarW v Nothing] | v <- vs]
@ -306,10 +341,11 @@ checkProcCallArgsUsage g = mapM_ checkArgs . findAllProcess isProcCall g
checkArgs :: (A.Process, (BK, UsageLabel)) -> m ()
checkArgs (p@(A.ProcCall m _ _), (bk, _))
= do vars <- getVarProcCall p
let mockedupParItems = ParItems [SeqItems [Usage Nothing Nothing Nothing v]
| v <- vars]
let mockedupParItems = fmap ((,) bk) $
ParItems [SeqItems [Usage Nothing Nothing Nothing v]
| v <- vars]
checkPlainVarUsage (m, mockedupParItems)
checkArrayUsage (m, fmap ((,) bk) mockedupParItems)
checkArrayUsage (m, mockedupParItems)
-- This isn't actually just unused variables, it's all unused names (except PROCs)
checkUnusedVar :: CheckOptM ()