1{-# LANGUAGE CPP #-}2{-# LANGUAGE Strict #-}3-- SPDX-FileCopyrightText: 2009 Matt Morrow <klebinger.andreas@gmx.at>4--5-- SPDX-License-Identifier: BSD-3-Clause6{-# OPTIONS_GHC -Wno-name-shadowing #-}78module Language.QBE.Analysis.Graph9 ( Node,10 Path,11 Edge,12 Graph,13 Rooted,14 idom,15 ipdom,16 domTree,17 pdomTree,18 dom,19 pdom,20 pddfs,21 rpddfs,22 fromAdj,23 fromEdges,24 toAdj,25 toEdges,26 asTree,27 asGraph,28 parents,29 ancestors,30 )31where3233import Control.Monad34import Control.Monad.ST.Strict35import Data.Array.Base36 ( unsafeNewArray_,37 unsafeRead,38 unsafeWrite,39 )40import Data.Array.ST41import Data.IntMap (IntMap)42import Data.IntMap.Strict qualified as IM43import Data.IntSet (IntSet)44import Data.IntSet qualified as IS45import Data.Maybe46import Data.Tree47import Data.Tuple (swap)4849-- Since GHC 9.10.1, Prelude exports foldl' before that we need 'Data.Foldable'.50--51-- See: https://gitlab.haskell.org/ghc/ghc/-/commit/f1ec362817baa5d440a9f2b3a8b17e551353811952#if !MIN_VERSION_base(4,20,0)53import Data.Foldable (foldl')54#endif5556-----------------------------------------------------------------------------5758type Node = Int5960type Path = [Node]6162type Edge = (Node, Node)6364type Graph = IntMap IntSet6566type Rooted = (Node, Graph)6768-----------------------------------------------------------------------------6970-- | /Dominators/.71-- Complexity as for @idom@72dom :: Rooted -> [(Node, Path)]73dom = ancestors . domTree7475-- | /Post-dominators/.76-- Complexity as for @idom@.77pdom :: Rooted -> [(Node, Path)]78pdom = ancestors . pdomTree7980-- | /Dominator tree/.81-- Complexity as for @idom@.82domTree :: Rooted -> Tree Node83domTree a@(r, _) =84 let is = filter ((/= r) . fst) (idom a)85 tg = fromEdges (fmap swap is)86 in asTree (r, tg)8788-- | /Post-dominator tree/.89-- Complexity as for @idom@.90pdomTree :: Rooted -> Tree Node91pdomTree a@(r, _) =92 let is = filter ((/= r) . fst) (ipdom a)93 tg = fromEdges (fmap swap is)94 in asTree (r, tg)9596-- | /Immediate dominators/.97-- /O(|E|*alpha(|E|,|V|))/, where /alpha(m,n)/ is98-- \"a functional inverse of Ackermann's function\".99--100-- This Complexity bound assumes /O(1)/ indexing. Since we're101-- using @IntMap@, it has an additional /lg |V|/ factor102-- somewhere in there. I'm not sure where.103idom :: Rooted -> [(Node, Node)]104idom rg = runST (evalS idomM =<< initEnv (pruneReach rg))105106-- | /Immediate post-dominators/.107-- Complexity as for @idom@.108ipdom :: Rooted -> [(Node, Node)]109ipdom rg = runST (evalS idomM =<< initEnv (pruneReach (second predG rg)))110111-----------------------------------------------------------------------------112113-- | /Post-dominated depth-first search/.114pddfs :: Rooted -> [Node]115pddfs = reverse . rpddfs116117-- | /Reverse post-dominated depth-first search/.118rpddfs :: Rooted -> [Node]119rpddfs = concat . levels . pdomTree120121-----------------------------------------------------------------------------122123type Dom s a = S s (Env s) a124125type NodeSet = IntSet126127type NodeMap a = IntMap a128129data Env s = Env130 { succE :: !Graph,131 predE :: !Graph,132 bucketE :: !Graph,133 dfsE :: {-# UNPACK #-} !Int,134 zeroE :: {-# UNPACK #-} !Node,135 rootE :: {-# UNPACK #-} !Node,136 labelE :: {-# UNPACK #-} !(Arr s Node),137 parentE :: {-# UNPACK #-} !(Arr s Node),138 ancestorE :: {-# UNPACK #-} !(Arr s Node),139 childE :: {-# UNPACK #-} !(Arr s Node),140 ndfsE :: {-# UNPACK #-} !(Arr s Node),141 dfnE :: {-# UNPACK #-} !(Arr s Int),142 sdnoE :: {-# UNPACK #-} !(Arr s Int),143 sizeE :: {-# UNPACK #-} !(Arr s Int),144 domE :: {-# UNPACK #-} !(Arr s Node),145 rnE :: {-# UNPACK #-} !(Arr s Node)146 }147148-----------------------------------------------------------------------------149150idomM :: Dom s [(Node, Node)]151idomM = do152 dfsDom =<< rootM153 n <- gets dfsE154 forM_155 [n, n - 1 .. 1]156 ( \i -> do157 w <- ndfsM i158 ps <- predsM w159 forM_160 ps161 ( \v -> do162 sw <- sdnoM w163 u <- eval v164 su <- sdnoM u165 when166 (su < sw)167 (store sdnoE w su)168 )169 z <- ndfsM =<< sdnoM w170 modify171 ( \e ->172 e173 { bucketE =174 IM.adjust175 (w `IS.insert`)176 z177 (bucketE e)178 }179 )180 pw <- parentM w181 link pw w182 bps <- bucketM pw183 forM_184 bps185 ( \v -> do186 u <- eval v187 su <- sdnoM u188 sv <- sdnoM v189 let dv = case su < sv of190 True -> u191 False -> pw192 store domE v dv193 )194 )195 forM_196 [1 .. n]197 ( \i -> do198 w <- ndfsM i199 j <- sdnoM w200 z <- ndfsM j201 dw <- domM w202 when203 (dw /= z)204 ( do205 ddw <- domM dw206 store domE w ddw207 )208 )209 fromEnv210211-----------------------------------------------------------------------------212213eval :: Node -> Dom s Node214eval v = do215 n0 <- zeroM216 a <- ancestorM v217 case a == n0 of218 True -> labelM v219 False -> do220 compress v221 a <- ancestorM v222 l <- labelM v223 la <- labelM a224 sl <- sdnoM l225 sla <- sdnoM la226 case sl <= sla of227 True -> return l228 False -> return la229230compress :: Node -> Dom s ()231compress v = do232 n0 <- zeroM233 a <- ancestorM v234 aa <- ancestorM a235 when236 (aa /= n0)237 ( do238 compress a239 a <- ancestorM v240 aa <- ancestorM a241 l <- labelM v242 la <- labelM a243 sl <- sdnoM l244 sla <- sdnoM la245 when246 (sla < sl)247 (store labelE v la)248 store ancestorE v aa249 )250251-----------------------------------------------------------------------------252253link :: Node -> Node -> Dom s ()254link v w = do255 n0 <- zeroM256 lw <- labelM w257 slw <- sdnoM lw258 let balance s = do259 c <- childM s260 lc <- labelM c261 slc <- sdnoM lc262 case slw < slc of263 False -> return s264 True -> do265 zs <- sizeM s266 zc <- sizeM c267 cc <- childM c268 zcc <- sizeM cc269 case 2 * zc <= zs + zcc of270 True -> do271 store ancestorE c s272 store childE s cc273 balance s274 False -> do275 store sizeE c zs276 store ancestorE s c277 balance c278 s <- balance w279 lw <- labelM w280 zw <- sizeM w281 store labelE s lw282 store sizeE v . (+ zw) =<< sizeM v283 let follow s = do284 when285 (s /= n0)286 ( do287 store ancestorE s v288 follow =<< childM s289 )290 zv <- sizeM v291 follow =<< case zv < 2 * zw of292 False -> return s293 True -> do294 cv <- childM v295 store childE v s296 return cv297298-----------------------------------------------------------------------------299300dfsDom :: Node -> Dom s ()301dfsDom i = do302 _ <- go i303 n0 <- zeroM304 r <- rootM305 store parentE r n0306 where307 go i = do308 n <- nextM309 store dfnE i n310 store sdnoE i n311 store ndfsE n i312 store labelE i i313 ss <- succsM i314 forM_315 ss316 ( \j -> do317 s <- sdnoM j318 case s == 0 of319 False -> return ()320 True -> do321 store parentE j i322 go j323 )324325-----------------------------------------------------------------------------326327initEnv :: Rooted -> ST s (Env s)328initEnv (r0, g0) = do329 -- Graph renumbered to indices from 1 to |V|330 let (g, rnmap) = renum 1 g0331 pred = predG g -- reverse graph332 root = rnmap IM.! r0 -- renamed root333 n = IM.size g334 ns = [0 .. n]335 m = n + 1336337 let bucket =338 IM.fromList339 (map (,mempty) ns)340341 rna <- newI m342 writes343 rna344 ( fmap345 swap346 (IM.toList rnmap)347 )348349 doms <- newI m350 sdno <- newI m351 size <- newI m352 parent <- newI m353 ancestor <- newI m354 child <- newI m355 label <- newI m356 ndfs <- newI m357 dfn <- newI m358359 -- Initialize all arrays360 forM_ [0 .. n] (doms .= 0)361 forM_ [0 .. n] (sdno .= 0)362 forM_ [1 .. n] (size .= 1)363 forM_ [0 .. n] (ancestor .= 0)364 forM_ [0 .. n] (child .= 0)365366 (doms .= root) root367 (size .= 0) 0368 (label .= 0) 0369370 return371 ( Env372 { rnE = rna,373 dfsE = 0,374 zeroE = 0,375 rootE = root,376 labelE = label,377 parentE = parent,378 ancestorE = ancestor,379 childE = child,380 ndfsE = ndfs,381 dfnE = dfn,382 sdnoE = sdno,383 sizeE = size,384 succE = g,385 predE = pred,386 bucketE = bucket,387 domE = doms388 }389 )390391fromEnv :: Dom s [(Node, Node)]392fromEnv = do393 dom <- gets domE394 rn <- gets rnE395 -- r <- gets rootE396 (_, n) <- st (getBounds dom)397 forM398 [1 .. n]399 ( \i -> do400 j <- st (rn !: i)401 d <- st (dom !: i)402 k <- st (rn !: d)403 return (j, k)404 )405406-----------------------------------------------------------------------------407408zeroM :: Dom s Node409zeroM = gets zeroE410411domM :: Node -> Dom s Node412domM = fetch domE413414rootM :: Dom s Node415rootM = gets rootE416417succsM :: Node -> Dom s [Node]418succsM i = gets (IS.toList . (! i) . succE)419420predsM :: Node -> Dom s [Node]421predsM i = gets (IS.toList . (! i) . predE)422423bucketM :: Node -> Dom s [Node]424bucketM i = gets (IS.toList . (! i) . bucketE)425426sizeM :: Node -> Dom s Int427sizeM = fetch sizeE428429sdnoM :: Node -> Dom s Int430sdnoM = fetch sdnoE431432-- dfnM :: Node -> Dom s Int433-- dfnM = fetch dfnE434ndfsM :: Int -> Dom s Node435ndfsM = fetch ndfsE436437childM :: Node -> Dom s Node438childM = fetch childE439440ancestorM :: Node -> Dom s Node441ancestorM = fetch ancestorE442443parentM :: Node -> Dom s Node444parentM = fetch parentE445446labelM :: Node -> Dom s Node447labelM = fetch labelE448449nextM :: Dom s Int450nextM = do451 n <- gets dfsE452 let n' = n + 1453 modify (\e -> e {dfsE = n'})454 return n'455456-----------------------------------------------------------------------------457458type A = STUArray459460type Arr s a = A s Int a461462infixl 9 !:463464infixr 2 .=465466-- | arr .= x idx => write x to index467(.=) ::468 (MArray (A s) a (ST s)) =>469 Arr s a -> a -> Int -> ST s ()470(v .= x) i = unsafeWrite v i x471472(!:) ::473 (MArray (A s) a (ST s)) =>474 A s Int a -> Int -> ST s a475a !: i = do476 o <- unsafeRead a i477 return $! o478479new ::480 (MArray (A s) a (ST s)) =>481 Int -> ST s (Arr s a)482new n = unsafeNewArray_ (0, n - 1)483484newI :: Int -> ST s (Arr s Int)485newI = new486487-- newD :: Int -> ST s (Arr s Double)488-- newD = new489490-- dump :: (MArray (A s) a (ST s)) => Arr s a -> ST s [a]491-- dump a = do492-- (m,n) <- getBounds a493-- forM [m..n] (\i -> a!:i)494495writes ::496 (MArray (A s) a (ST s)) =>497 Arr s a -> [(Int, a)] -> ST s ()498writes a xs = forM_ xs (\(i, x) -> (a .= x) i)499500-- arr :: (MArray (A s) a (ST s)) => [a] -> ST s (Arr s a)501-- arr xs = do502-- let n = length xs503-- a <- new n504-- go a n 0 xs505-- return a506-- where go _ _ _ [] = return ()507-- go a n i (x:xs)508-- | i <= n = (a.=x) i >> go a n (i+1) xs509-- | otherwise = return ()510511-----------------------------------------------------------------------------512513(!) :: (Monoid a) => IntMap a -> Int -> a514(!) g n = fromMaybe mempty (IM.lookup n g)515516fromAdj :: [(Node, [Node])] -> Graph517fromAdj = IM.fromList . fmap (second IS.fromList)518519fromEdges :: [Edge] -> Graph520fromEdges = collectI IS.union fst (IS.singleton . snd)521522toAdj :: Graph -> [(Node, [Node])]523toAdj = fmap (second IS.toList) . IM.toList524525toEdges :: Graph -> [Edge]526toEdges = concatMap (uncurry (fmap . (,))) . toAdj527528predG :: Graph -> Graph529predG g = IM.unionWith IS.union (go g) g0530 where531 g0 = fmap (const mempty) g532 go =533 IM.foldrWithKey534 ( \i a m ->535 foldl'536 ( \m p ->537 IM.insertWith538 mappend539 p540 (IS.singleton i)541 m542 )543 m544 (IS.toList a)545 )546 mempty547548-- predG :: Graph -> Graph549-- predG g = IM.unionWith IS.union (go g) g0550-- where g0 = fmap (const mempty) g551-- f :: IntMap IntSet -> Int -> IntSet -> IntMap IntSet552-- f m i a = foldl' (\m p -> IM.insertWith mappend p553-- (IS.singleton i) m)554-- m555-- (IS.toList a)556-- go :: IntMap IntSet -> IntMap IntSet557-- go = flip IM.foldlWithKey' mempty f558559pruneReach :: Rooted -> Rooted560pruneReach (r, g) = (r, g2)561 where562 is =563 reachable564 ( fromMaybe mempty565 . flip IM.lookup g566 )567 r568 g2 =569 IM.map (IS.filter (`IS.member` is))570 . IM.filterWithKey (\node _targets -> IS.member node is)571 $ g572573tip :: Tree a -> (a, [Tree a])574tip (Node a ts) = (a, ts)575576parents :: Tree a -> [(a, a)]577parents (Node i xs) =578 p i xs579 ++ concatMap parents xs580 where581 p i = fmap ((,i) . rootLabel)582583ancestors :: Tree a -> [(a, [a])]584ancestors = go []585 where586 go acc (Node i xs) =587 let acc' = i : acc588 in p acc' xs ++ concatMap (go acc') xs589 p is = fmap ((,is) . rootLabel)590591asGraph :: Tree Node -> Rooted592asGraph t@(Node a _) = let g = go t in (a, fromAdj g)593 where594 go (Node a ts) =595 let as = (map fst . fmap tip) ts596 in (a, as) : concatMap go ts597598asTree :: Rooted -> Tree Node599asTree (r, g) =600 let go a = Node a (fmap go ((IS.toList . f) a))601 f = (g !)602 in go r603604reachable :: (Node -> NodeSet) -> (Node -> NodeSet)605reachable f a = go (IS.singleton a) a606 where607 go seen a =608 let s = f a609 as = IS.toList (s `IS.difference` seen)610 in foldl' go (s `IS.union` seen) as611612collectI ::613 (c -> c -> c) ->614 (a -> Int) ->615 (a -> c) ->616 [a] ->617 IntMap c618collectI (<>) f g =619 foldl'620 ( \m a ->621 IM.insertWith622 (<>)623 (f a)624 (g a)625 m626 )627 mempty628629-- collect :: (Ord b) => (c -> c -> c)630-- -> (a -> b) -> (a -> c) -> [a] -> Map b c631-- collect (<>) f g632-- = foldl' (\m a -> SM.insertWith (<>)633-- (f a)634-- (g a) m) mempty635636-- | renum n g: Rename all nodes637--638-- Gives nodes sequential names starting at n.639-- Returns the new graph and a mapping.640-- (renamed, old -> new)641renum :: Int -> Graph -> (Graph, NodeMap Node)642renum from =643 (\(_, m, g) -> (g, m))644 . IM.foldrWithKey645 ( \i ss (!n, !env, !new) ->646 let (j, n2, env2) = go n env i647 (n3, env3, ss2) =648 IS.fold649 ( \k (!n, !env, !new) ->650 case go n env k of651 (l, n2, env2) -> (n2, env2, l `IS.insert` new)652 )653 (n2, env2, mempty)654 ss655 new2 = IM.insertWith IS.union j ss2 new656 in (n3, env3, new2)657 )658 (from, mempty, mempty)659 where660 go ::661 Int ->662 NodeMap Node ->663 Node ->664 (Node, Int, NodeMap Node)665 go !n !env i =666 case IM.lookup i env of667 Just j -> (j, n, env)668 Nothing -> (n, n + 1, IM.insert i n env)669670-----------------------------------------------------------------------------671672-- Nothing better than reinvinting the state monad.673newtype S z s a = S {unS :: forall o. (a -> s -> ST z o) -> s -> ST z o}674675instance Functor (S z s) where676 fmap f (S g) = S (\k -> g (k . f))677678instance Monad (S z s) where679 return = pure680 S g >>= f = S (\k -> g (\a -> unS (f a) k))681682instance Applicative (S z s) where683 pure a = S (\k -> k a)684 (<*>) = ap685686-- get :: S z s s687-- get = S (\k s -> k s s)688gets :: (s -> a) -> S z s a689gets f = S (\k s -> k (f s) s)690691-- set :: s -> S z s ()692-- set s = S (\k _ -> k () s)693modify :: (s -> s) -> S z s ()694modify f = S (\k -> k () . f)695696-- runS :: S z s a -> s -> ST z (a, s)697-- runS (S g) = g (\a s -> return (a,s))698evalS :: S z s a -> s -> ST z a699evalS (S g) = g ((return .) . const)700701-- execS :: S z s a -> s -> ST z s702-- execS (S g) = g ((return .) . flip const)703st :: ST z a -> S z s a704st m =705 S706 ( \k s -> do707 a <- m708 k a s709 )710711store ::712 (MArray (A z) a (ST z)) =>713 (s -> Arr z a) -> Int -> a -> S z s ()714store f i x = do715 a <- gets f716 st ((a .= x) i)717718fetch ::719 (MArray (A z) a (ST z)) =>720 (s -> Arr z a) -> Int -> S z s a721fetch f i = do722 a <- gets f723 st (a !: i)724725-- Redefine Data.Bifunctor.second for GHC 7 compatibility726second :: (b -> c) -> (a, b) -> (a, c)727second f (a, b) = (a, f b)