module Language.Boogie.DataFlow (liveVariables, liveInputVariables) where
import Language.Boogie.AST
import Language.Boogie.Util
import Language.Boogie.Position hiding (gen)
import Language.Boogie.BasicBlocks
import Data.List
import Data.Map (Map, (!))
import qualified Data.Map as M
import Data.Set (Set)
import qualified Data.Set as S
liveInputVariables :: PSig -> PDef -> ([Id], [Id])
liveInputVariables sig def = let
body = pdefBody def
liveVars = liveVariables (attachContractChecks sig def)
liveLocals = filter (`elem` liveVars) (map itwId (fst body))
liveIns = filter (`elem` liveVars) (pdefIns def)
liveOuts = filter (`elem` liveVars) (pdefOuts def)
liveGlobals = liveVars \\ (liveLocals ++ liveIns ++ liveOuts)
in (liveIns, liveGlobals)
liveVariables :: Map Id [Statement] -> [Id]
liveVariables body = let
empty = M.map (const S.empty) body
insertExitBlock i = M.insert i (transition (body ! i) S.empty)
entry0 = S.foldr insertExitBlock empty (exitBlocks body)
changed0 = M.keysSet body <-> exitBlocks body
oldVariables = S.unions (map (\block -> S.unions (map genOld block)) (M.elems body))
in S.toList (oldVariables `S.union` (analyse body entry0 empty changed0 ! startLabel))
analyse :: Map Id [Statement] -> Map Id (Set Id) -> Map Id (Set Id) -> Set Id -> Map Id (Set Id)
analyse body entry exit changed = if S.null changed
then entry
else let
(i, changed') = S.deleteFindMax changed
newExit = setUnions $ S.map (entry !) (successors body i)
newEntry = transition (body ! i) newExit
exit' = M.insert i newExit exit
entry' = M.insert i newEntry entry
changed'' = if entry ! i == newEntry then changed' else changed' <+> predecessors body i
in analyse body entry' exit' changed''
(<+>) = (S.union)
(<->) = (S.\\)
setUnions sets = S.foldl S.union S.empty sets
transition :: [Statement] -> Set Id -> Set Id
transition sts exit = foldr transition1 exit sts
where
transition1 st exit = exit <-> kill st <+> gen st
kill :: Statement -> Set Id
kill st = case node st of
Havoc ids -> S.fromList ids
Assign lhss _ -> S.fromList (map fst lhss)
Call lhss _ _ -> S.fromList lhss
otherwise -> S.empty
gen :: Statement -> Set Id
gen st = genTwoState fst st
genOld :: Statement -> Set Id
genOld st = genTwoState snd st
genTwoState :: (([Id], [Id]) -> [Id]) -> Statement -> Set Id
genTwoState select st = case node st of
Predicate (SpecClause _ _ e) -> (S.fromList . select . freeVarsTwoState) e
Assign lhss rhss -> let
allSubscipts = concat $ concatMap snd lhss
subsciptedLhss = [fst lhs | lhs <- lhss, not (null (snd lhs))]
in S.unions (map (S.fromList . select . freeVarsTwoState) (rhss ++ allSubscipts)) <+> S.fromList subsciptedLhss
Call _ _ args -> S.unions (map (S.fromList . select . freeVarsTwoState) args)
CallForall _ args -> S.unions (map (S.fromList . select . freeVarsTwoState') args)
otherwise -> S.empty
where
freeVarsTwoState' Wildcard = ([], [])
freeVarsTwoState' (Expr e) = freeVarsTwoState e
exitBlocks :: Map Id [Statement] -> Set Id
exitBlocks body = M.keysSet $ M.filter isExit body
where
isExit block = case node (last block) of
Return -> True
_ -> False
predecessors :: Map Id [Statement] -> Id -> Set Id
predecessors body label = M.keysSet $ M.filter (goesTo label) body
where
goesTo label block = case node (last block) of
Goto lbs -> label `elem` lbs
_ -> False
successors :: Map Id [Statement] -> Id -> Set Id
successors body label = case node (last (body ! label)) of
Goto lbs -> S.fromList lbs
_ -> S.empty
attachContractChecks :: PSig -> PDef -> Map Id [Statement]
attachContractChecks sig def = let
preChecks = map (attachPos (pdefPos def) . Predicate . subst sig) (psigRequires sig)
postChecks = map (attachPos (pdefPos def) . Predicate . subst sig) (psigEnsures sig)
subst sig (SpecClause t f e) = SpecClause t f (paramSubst sig def e)
attachPreChecks = M.adjust (preChecks ++) startLabel (snd (pdefBody def))
attachPostChecks block = let jump = last block
in case node jump of
Return -> init block ++ postChecks ++ [jump]
_ -> block
in M.map attachPostChecks attachPreChecks