module Language.Fortran.Analysis.DataFlow
( dominators, iDominators, DomMap, IDomMap
, postOrder, revPostOrder, preOrder, revPreOrder, OrderF
, dataFlowSolver, showDataFlow, InOut, InOutMap, InF, OutF
, liveVariableAnalysis, reachingDefinitions
, genUDMap, genDUMap, duMapToUdMap, UDMap, DUMap
, genFlowsToGraph, FlowsGraph
, genVarFlowsToMap, VarFlowsMap
, genBlockMap, genDefMap, BlockMap, DefMap
, genCallMap, CallMap
, loopNodes, genBackEdgeMap, sccWith, BackEdgeMap
, genLoopNodeMap, LoopNodeMap
, genInductionVarMap, InductionVarMap
, genInductionVarMapByASTBlock, InductionVarMapByASTBlock
, noPredNodes, genDerivedInductionMap, DerivedInductionMap, InductionExpr(..)
) where
import Data.Generics.Uniplate.Data
import Data.Generics.Uniplate.Operations
import GHC.Generics
import Data.Data
import Data.Function
import Control.Monad.State.Lazy
import Control.Monad.Writer
import Text.PrettyPrint.GenericPretty (pretty, Out)
import Language.Fortran.Parser.Utils
import Language.Fortran.Analysis
import Language.Fortran.Analysis.BBlocks
import Language.Fortran.AST
import qualified Data.Map as M
import qualified Data.IntMap.Lazy as IM
import qualified Data.Set as S
import qualified Data.IntSet as IS
import Data.Graph.Inductive hiding (trc, dom)
import Data.Graph.Inductive.PatriciaTree (Gr)
import Data.Graph.Inductive.Query.BFS (bfen)
import Data.Maybe
import Data.List (foldl', foldl1', (\\), union, delete, nub, intersect)
import qualified Debug.Trace as D
type DomMap = IM.IntMap IS.IntSet
dominators :: BBGr a -> DomMap
dominators gr = IM.map snd $ dataFlowSolver gr init revPostOrder inn out
where
nodeSet = IS.fromList $ nodes gr
init n = (nodeSet, nodeSet)
inn outF n
| preNodes@(_:_) <- pre gr n = foldl1' IS.intersection . map outF $ preNodes
| otherwise = IS.empty
out inF n = IS.insert n $ inF n
type IDomMap = IM.IntMap Int
iDominators :: BBGr a -> IDomMap
iDominators gr = IM.unions [ IM.fromList . flip iDom n $ gr | n <- noPredNodes gr ]
type OrderF a = BBGr a -> [Node]
postOrder :: OrderF a
postOrder gr = concatMap postorder . dff (noPredNodes gr) $ gr
revPostOrder :: OrderF a
revPostOrder = reverse . postOrder
preOrder :: OrderF a
preOrder gr = concatMap preorder . dff (noPredNodes gr) $ gr
revPreOrder :: OrderF a
revPreOrder = reverse . preOrder
noPredNodes :: Graph g => g a b -> [Node]
noPredNodes gr = filter (null . pre gr) (nodes gr)
type InOut t = (t, t)
type InOutMap t = IM.IntMap (InOut t)
type InF t = Node -> t
type OutF t = Node -> t
dataFlowSolver :: Ord t => BBGr a
-> (Node -> InOut t)
-> OrderF a
-> (OutF t -> InF t)
-> (InF t -> OutF t)
-> InOutMap t
dataFlowSolver gr initF order inF outF = converge (==) $ iterate step initM
where
ordNodes = order gr
initM = IM.fromList [ (n, initF n) | n <- ordNodes ]
step m = IM.fromList [ (n, (inF (snd . get m) n, outF (fst . get m) n)) | n <- ordNodes ]
get m n = fromJustMsg ("dataFlowSolver: get " ++ show (n)) $ IM.lookup n m
dataFlowSolver' :: Ord t => BBGr a
-> (Node -> InOut t)
-> OrderF a
-> (OutF t -> InF t)
-> (InF t -> OutF t)
-> [InOutMap t]
dataFlowSolver' gr initF order inF outF = iterate step initM
where
ordNodes = order gr
initM = IM.fromList [ (n, initF n) | n <- ordNodes ]
step m = IM.fromList [ (n, (inF (snd . get m) n, outF (fst . get m) n)) | n <- ordNodes ]
get m n = fromJustMsg ("dataFlowSolver': get " ++ show (n)) $ IM.lookup n m
type BlockMap a = IM.IntMap (Block (Analysis a))
genBlockMap :: Data a => ProgramFile (Analysis a) -> BlockMap a
genBlockMap pf = IM.fromList [ (i, b) | gr <- uni pf
, (_, bs) <- labNodes gr
, b <- bs
, let Just i = insLabel (getAnnotation b) ]
where
uni :: Data a => ProgramFile (Analysis a) -> [BBGr (Analysis a)]
uni = universeBi
type DefMap = M.Map Name IS.IntSet
genDefMap :: Data a => BlockMap a -> DefMap
genDefMap bm = M.fromListWith IS.union [
(y, IS.singleton i) | (i, b) <- IM.toList bm, y <- allLhsVars b
]
liveVariableAnalysis :: Data a => BBGr (Analysis a) -> InOutMap (S.Set Name)
liveVariableAnalysis gr = dataFlowSolver gr (const (S.empty, S.empty)) revPreOrder inn out
where
inn outF b = (outF b S.\\ kill b) `S.union` gen b
out innF b = S.unions [ innF s | s <- suc gr b ]
kill b = bblockKill (fromJustMsg "liveVariableAnalysis kill" $ lab gr b)
gen b = bblockGen (fromJustMsg "liveVariableAnalysis gen" $ lab gr b)
bblockKill :: Data a => [Block (Analysis a)] -> S.Set Name
bblockKill = S.fromList . concatMap blockKill
bblockGen :: Data a => [Block (Analysis a)] -> S.Set Name
bblockGen bs = S.fromList . fst . foldl' f ([], []) $ zip (map blockGen bs) (map blockKill bs)
where
f (bbgen, bbkill) (gen, kill) = ((gen \\ bbkill) `union` bbgen, kill `union` bbkill)
bblockGenFast :: Data a => [Block (Analysis a)] -> S.Set Name
bblockGenFast bs = fst . foldl' f (S.empty, S.empty) $ zip (map (S.fromList . blockGen) bs) (map (S.fromList . blockKill) bs)
where
f (bbgen, bbkill) (gen, kill) = ((gen S.\\ bbkill) `S.union` bbgen, kill `S.union` bbkill)
blockKill :: Data a => Block (Analysis a) -> [Name]
blockKill = blockVarDefs
blockGen :: Data a => Block (Analysis a) -> [Name]
blockGen = blockVarUses
reachingDefinitions :: Data a => DefMap -> BBGr (Analysis a) -> InOutMap IS.IntSet
reachingDefinitions dm gr = dataFlowSolver gr (const (IS.empty, IS.empty)) revPostOrder inn out
where
inn outF b = IS.unions [ outF s | s <- pre gr b ]
out innF b = gen `IS.union` (innF b IS.\\ kill)
where (gen, kill) = rdBblockGenKill dm (fromJustMsg "reachingDefinitions" $ lab gr b)
rdBblockGenKill :: Data a => DefMap -> [Block (Analysis a)] -> (IS.IntSet, IS.IntSet)
rdBblockGenKill dm bs = foldl' f (IS.empty, IS.empty) $ zip (map gen bs) (map kill bs)
where
gen b | null (allLhsVars b) = IS.empty
| otherwise = IS.singleton . fromJustMsg "rdBblockGenKill" . insLabel . getAnnotation $ b
kill = rdDefs dm
f (bbgen, bbkill) (gen, kill) =
((bbgen IS.\\ kill) `IS.union` gen, (bbkill IS.\\ gen) `IS.union` kill)
rdDefs :: Data a => DefMap -> Block (Analysis a) -> IS.IntSet
rdDefs dm b = IS.unions [ IS.empty `fromMaybe` M.lookup y dm | y <- allLhsVars b ]
type DUMap = IM.IntMap IS.IntSet
genDUMap :: Data a => BlockMap a -> DefMap -> BBGr (Analysis a) -> InOutMap IS.IntSet -> DUMap
genDUMap bm dm gr rdefs = IM.unionsWith IS.union duMaps
where
duMaps = [ fst (foldl' inBBlock (IM.empty, is) bs) |
(n, (is, _)) <- IM.toList rdefs,
let Just bs = lab gr n ]
inBBlock (duMap, inSet) b = (duMap', inSet')
where
Just i = insLabel (getAnnotation b)
bduMap = IM.fromListWith IS.union [ (i', IS.singleton i) | i' <- IS.toList inSet, overlap i' ]
overlap i' = not . null . intersect uses $ blockVarDefs b'
where Just b' = IM.lookup i' bm
uses = blockVarUses b
duMap' = IM.unionWith IS.union duMap bduMap
gen b | null (allLhsVars b) = IS.empty
| otherwise = IS.singleton . fromJustMsg "genDUMap" . insLabel . getAnnotation $ b
kill = rdDefs dm
inSet' = (inSet IS.\\ (kill b)) `IS.union` (gen b)
type UDMap = IM.IntMap IS.IntSet
duMapToUdMap :: DUMap -> UDMap
duMapToUdMap duMap = IM.fromListWith IS.union [
(use, IS.singleton def) | (def, uses) <- IM.toList duMap, use <- IS.toList uses
]
genUDMap :: Data a => BlockMap a -> DefMap -> BBGr (Analysis a) -> InOutMap IS.IntSet -> UDMap
genUDMap bm dm gr = duMapToUdMap . genDUMap bm dm gr
mapToGraph :: DynGraph gr => IM.IntMap a -> IM.IntMap IS.IntSet -> gr a ()
mapToGraph bm m = buildGr $ [
([], i, l, jAdj) | (i, js) <- IM.toList m
, let Just l = IM.lookup i bm
, let jAdj = map ((),) $ IS.toList js
] ++ [
(iAdj, j, l, []) | (i, js) <- IM.toList m
, j <- IS.toList js
, let Just l = IM.lookup j bm
, let iAdj = [((), i)]
]
type FlowsGraph a = Gr (Block (Analysis a)) ()
genFlowsToGraph :: Data a => BlockMap a
-> DefMap
-> BBGr (Analysis a)
-> InOutMap IS.IntSet
-> FlowsGraph a
genFlowsToGraph bm dm gr = mapToGraph bm . genDUMap bm dm gr
type VarFlowsMap = M.Map Name (S.Set Name)
genVarFlowsToMap :: Data a => DefMap -> FlowsGraph a -> VarFlowsMap
genVarFlowsToMap dm fg = M.fromListWith S.union [ (conv u, sconv v) | (u, v) <- edges fg ]
where
sconv i | Just v <- IM.lookup i revDM = S.singleton v
| otherwise = S.empty
conv i | Just v <- IM.lookup i revDM = v
| otherwise = error $ "genVarFlowsToMap: convert failed, i=" ++ show i
revDM = IM.fromListWith (curry fst) [ (i, v) | (v, is) <- M.toList dm, i <- IS.toList is ]
tc :: (DynGraph gr) => gr a b -> gr a ()
tc g = newEdges `insEdges` insNodes ln empty
where
ln = labNodes g
newEdges = [ toLEdge (u, v) () | (u, _) <- ln, (_, v) <- bfen (outU g u) g ]
outU gr = map toEdge . out gr
type BackEdgeMap = IM.IntMap Node
genBackEdgeMap :: Graph gr => DomMap -> gr a b -> BackEdgeMap
genBackEdgeMap domMap = IM.fromList . filter isBackEdge . edges
where
isBackEdge (s, t) = t `IS.member` (fromJustMsg "genBackEdgeMap" $ s `IM.lookup` domMap)
loopNodes :: Graph gr => BackEdgeMap -> gr a b -> [IS.IntSet]
loopNodes bedges gr = [
IS.fromList (n:intersect (sccWith n gr) (rdfs [m] (delNode n gr))) | (m, n) <- IM.toList bedges
]
type LoopNodeMap = IM.IntMap IS.IntSet
genLoopNodeMap :: Graph gr => BackEdgeMap -> gr a b -> LoopNodeMap
genLoopNodeMap bedges gr = IM.fromList [
(n, IS.fromList (n:intersect (sccWith n gr) (rdfs [m] (delNode n gr)))) | (m, n) <- IM.toList bedges
]
sccWith :: (Graph gr) => Node -> gr a b -> [Node]
sccWith n g = case filter (n `elem`) $ scc g of
[] -> []
c:_ -> c
type InductionVarMap = IM.IntMap (S.Set Name)
basicInductionVars :: Data a => BackEdgeMap -> BBGr (Analysis a) -> InductionVarMap
basicInductionVars bedges gr = IM.fromListWith S.union [
(n, S.singleton v) | (_, n) <- IM.toList bedges
, let Just bs = lab gr n
, b@(BlDo {}) <- bs
, v <- blockVarDefs b
]
genInductionVarMap :: Data a => BackEdgeMap -> BBGr (Analysis a) -> InductionVarMap
genInductionVarMap = basicInductionVars
type InductionVarMapByASTBlock = IM.IntMap (S.Set Name)
genInductionVarMapByASTBlock :: forall a. Data a => BackEdgeMap -> BBGr (Analysis a) -> InductionVarMapByASTBlock
genInductionVarMapByASTBlock bedges gr = loopsToLabs . genInductionVarMap bedges $ gr
where
lnMap = genLoopNodeMap bedges gr
get = fromMaybe (error "missing loop-header node") . flip IM.lookup lnMap
astLabels n = [ i | b <- (universeBi :: Maybe [Block (Analysis a)] -> [Block (Analysis a)]) (lab gr n)
, let Just i = insLabel (getAnnotation b) ]
loopsToLabs = IM.fromListWith S.union . concatMap loopToLabs . IM.toList
loopToLabs (n, ivs) = (map (,ivs) . astLabels) =<< IS.toList (get n)
data InductionExpr
= IETop
| IELinear Name Int Int
| IEBottom
deriving (Show, Eq, Ord, Typeable, Generic, Data)
type DerivedInductionMap = IM.IntMap InductionExpr
data IEFlow = IEFlow { ieFlowVars :: M.Map Name InductionExpr, ieFlowExprs :: DerivedInductionMap }
deriving (Show, Eq, Ord, Typeable, Generic, Data)
ieFlowInsertVar v ie flow = flow { ieFlowVars = M.insert v ie (ieFlowVars flow) }
ieFlowInsertExpr i ie flow = flow { ieFlowExprs = IM.insert i ie (ieFlowExprs flow) }
emptyIEFlow = IEFlow M.empty IM.empty
joinIEFlows flows = IEFlow flowV flowE
where
flowV = M.unionsWith joinInductionExprs (map ieFlowVars flows)
flowE = IM.unionsWith joinInductionExprs (map ieFlowExprs flows)
genDerivedInductionMap :: forall a. Data a => BackEdgeMap -> BBGr (Analysis a) -> DerivedInductionMap
genDerivedInductionMap bedges gr = ieFlowExprs . joinIEFlows . map snd . IM.elems . IM.filterWithKey inLoop $ inOutMaps
where
bivMap = basicInductionVars bedges gr
loopNodeSet = IS.unions (loopNodes bedges gr)
inLoop i _ = i `IS.member` loopNodeSet
step :: IEFlow -> Block (Analysis a) -> IEFlow
step flow b = case b of
BlStatement _ _ _ (StExpressionAssign _ _ lv@(ExpValue _ _ (ValVariable _)) rhs)
| rhsLabel <- insLabel (getAnnotation rhs)
, flow'' <- ieFlowInsertVar (varName lv) (derivedInductionExpr flow' rhs) flow' -> stepExpr flow'' lv
_ -> flow'
where
flow' = foldl' stepExpr flow (universeBi b)
stepExpr :: IEFlow -> Expression (Analysis a) -> IEFlow
stepExpr flow e = ieFlowInsertExpr label ie flow
where
ie = derivedInductionExpr flow e
label = fromJustMsg "stepExpr" $ insLabel (getAnnotation e)
out :: InF IEFlow -> OutF IEFlow
out inF node = foldl' step flow (fromJustMsg ("analyseDerivedIE out(" ++ show node ++ ")") $ lab gr node)
where
flow = joinIEFlows [fst (initF node), inF node]
inn :: OutF IEFlow -> InF IEFlow
inn outF node = joinIEFlows [ outF p | p <- pre gr node ]
initF :: Node -> InOut IEFlow
initF node = case IM.lookup node bivMap of
Just set -> (IEFlow (M.fromList [ (n, IELinear n 1 0) | n <- S.toList set ]) IM.empty, emptyIEFlow)
Nothing -> (emptyIEFlow, emptyIEFlow)
inOutMaps = dataFlowSolver gr initF revPostOrder inn out
derivedInductionExpr :: Data a => IEFlow -> Expression (Analysis a) -> InductionExpr
derivedInductionExpr flow e = case e of
v@(ExpValue _ _ (ValVariable _)) -> fromMaybe IETop $ M.lookup (varName v) (ieFlowVars flow)
ExpValue _ _ (ValInteger str)
| Just i <- readInteger str -> IELinear "" 0 (fromIntegral i)
ExpBinary _ _ Addition e1 e2 -> derive e1 `addInductionExprs` derive e2
ExpBinary _ _ Subtraction e1 e2 -> derive e1 `addInductionExprs` negInductionExpr (derive e2)
ExpBinary _ _ Multiplication e1 e2 -> derive e1 `mulInductionExprs` derive e2
_ -> IETop
where
derive = derivedInductionExpr flow
addInductionExprs :: InductionExpr -> InductionExpr -> InductionExpr
addInductionExprs (IELinear ln lc lo) (IELinear rn rc ro)
| ln == rn = IELinear ln (lc + rc) (lo + ro)
| lc == 0 = IELinear rn rc (lo + ro)
| rc == 0 = IELinear ln lc (lo + ro)
| otherwise = IEBottom
addInductionExprs ie1 IETop = IETop
addInductionExprs IETop ie2 = IETop
addInductionExprs _ _ = IEBottom
negInductionExpr :: InductionExpr -> InductionExpr
negInductionExpr (IELinear n c o) = IELinear n (c) (o)
negInductionExpr IETop = IETop
negInductionExpr _ = IEBottom
mulInductionExprs :: InductionExpr -> InductionExpr -> InductionExpr
mulInductionExprs (IELinear "" lc lo) (IELinear rn rc ro) = IELinear rn (rc * lo) (ro * lo)
mulInductionExprs (IELinear ln lc lo) (IELinear "" rc ro) = IELinear ln (lc * ro) (lo * ro)
mulInductionExprs _ IETop = IETop
mulInductionExprs IETop _ = IETop
mulInductionExprs _ _ = IEBottom
joinInductionExprs :: InductionExpr -> InductionExpr -> InductionExpr
joinInductionExprs ie1 IETop = ie1
joinInductionExprs IETop ie2 = ie2
joinInductionExprs ie1 ie2
| ie1 == ie2 = ie1
| otherwise = IEBottom
showDataFlow :: (Data a, Out a, Show a) => ProgramFile (Analysis a) -> String
showDataFlow pf = perPU =<< uni pf
where
uni = (universeBi :: Data a => ProgramFile (Analysis a) -> [ProgramUnit (Analysis a)])
perPU pu | Analysis { bBlocks = Just gr } <- getAnnotation pu =
dashes ++ "\n" ++ p ++ "\n" ++ dashes ++ "\n" ++ dfStr gr ++ "\n\n"
where p = "| Program Unit " ++ show (puName pu) ++ " |"
dashes = replicate (length p) '-'
dfStr gr = (\ (l, x) -> '\n':l ++ ": " ++ x) =<< [
("callMap", show cm)
, ("postOrder", show (postOrder gr))
, ("revPostOrder", show (revPostOrder gr))
, ("revPreOrder", show (revPreOrder gr))
, ("dominators", show (dominators gr))
, ("iDominators", show (iDominators gr))
, ("defMap", show dm)
, ("lva", show (IM.toList $ lva gr))
, ("rd", show (IM.toList $ rd gr))
, ("backEdges", show bedges)
, ("topsort", show (topsort gr))
, ("scc ", show (scc gr))
, ("loopNodes", show (loopNodes bedges gr))
, ("duMap", show (genDUMap bm dm gr (rd gr)))
, ("udMap", show (genUDMap bm dm gr (rd gr)))
, ("flowsTo", show (edges $ genFlowsToGraph bm dm gr (rd gr)))
, ("varFlowsTo", show (genVarFlowsToMap dm (genFlowsToGraph bm dm gr (rd gr))))
, ("ivMap", show (genInductionVarMap bedges gr))
, ("ivMapByAST", show (genInductionVarMapByASTBlock bedges gr))
, ("noPredNodes", show (noPredNodes gr))
] where
bedges = genBackEdgeMap (dominators gr) gr
perPU _ = ""
lva = liveVariableAnalysis
bm = genBlockMap pf
dm = genDefMap bm
rd = reachingDefinitions dm
cm = genCallMap pf
type CallMap = M.Map ProgramUnitName (S.Set Name)
genCallMap :: Data a => ProgramFile (Analysis a) -> CallMap
genCallMap pf = flip execState M.empty $ do
let uP = (universeBi :: Data a => ProgramFile a -> [ProgramUnit a])
forM_ (uP pf) $ \ pu -> do
let n = puName pu
let uS :: Data a => ProgramUnit a -> [Statement a]
uS = universeBi
let uE :: Data a => ProgramUnit a -> [Expression a]
uE = universeBi
m <- get
let ns = [ varName v | StCall _ _ v@(ExpValue _ _ _) _ <- uS pu ] ++
[ varName v | ExpFunctionCall _ _ v@(ExpValue _ _ _) _ <- uE pu ]
put $ M.insert n (S.fromList ns) m
converge :: (a -> a -> Bool) -> [a] -> a
converge p (x:ys@(y:_))
| p x y = y
| otherwise = converge p ys
fromJustMsg _ (Just x) = x
fromJustMsg msg _ = error msg