packages feed

optimusprime-0.0.1.20091114: Optimus/CallGraph.hs

module Optimus.CallGraph where
	import Flite.Pretty
	import Flite.Syntax
	import Flite.Traversals
	import Optimus.Uniplate
	import Optimus.Util
	
	import Control.Monad
	import Control.Monad.Fix
	import Control.Monad.State
	import qualified Data.Map as Map
	import qualified Data.Set as Set
	import Data.Generics.Uniplate
	import Data.Graph.Inductive.Graph
	import Data.Graph.Inductive.PatriciaTree
	import Data.GraphViz hiding (Int)
	import Data.List
	import Data.Maybe
	import Debug.Trace
	
	callGraph :: Prog -> Gr Id Id
	callGraph p = mkGraph (zip [1..] nodes) edges
		where
			nodes = nub $ [ f | Func f _ _ <- p ]
			lookupNodes = flip lookup (zip nodes [1..])
			edges = nub . catMaybes $ [ liftM2 (\x y -> (x, y, "")) (lookupNodes f) (lookupNodes g) | Func f _ r <- p, Fun g <- universe r ]
	
	conGraph :: Prog -> Gr Id [Id]
	conGraph p = mkGraph (zip [1..] nodes) edges
		where
			nodes = nub $ [ f | Func f _ _ <- p ]
			lookupNodes = flip lookup (zip nodes [1..])
			edges = nub . catMaybes $ [ liftM2 (\x y -> (x, y, consUsed r)) (lookupNodes f) (lookupNodes g) | Func f _ r <- p, Fun g <- universe r ]
			consUsed e = nub $ [ c | Con c <- universe e ] ++ [ c | Case _ as <- universe e, (p', _) <- as, Con c <- universe p' ]
	
	produceDot :: (Graph gr, Show b) => gr Id b -> String
	produceDot g = printDotGraph $ graphToDot True g [] labelNode labelEdge
		where
			labelNode (_, l) = [Label (StrLabel l), Color [ColorName $ if words l !! 0 `elem` primitives then "black" else "red"]]
			labelEdge (_, _, l) = if show l == "\"\"" then [] else [Label (StrLabel $ show l)]
			
	-----------------------
	
	arityGraph :: Prog -> Gr Id Id
	arityGraph p = mkGraph (zip [1..] nodes) edges
		where
			nodes = nub $ [ f | Func f _ _ <- p ]
			lookupNodes = flip lookup (zip nodes [1..])
			arities = [ (f, length a) | Func f a _ <- p ] ++ map (flip (,) 2) primitives
			arity f = fromMaybe (error "Missing function definition") $ lookup f arities
			edges = nub . catMaybes $ [ liftM2 (\x y -> (x, y, "")) (lookupNodes f) (lookupNodes g) | Func f _ r <- p, App (Fun g) ys <- universe r, arity f <= length ys ]
	
	hofGraph :: Prog -> Gr Id Id
	hofGraph p = mkGraph (zip [1..] nodes) edges
		where
			nodes = nub $ [ f | Func f _ _ <- p ]
						 ++ [ concat $ intersperse " " $ g : [ if hof e then showArg e else "_" | e <- ys ]
							| Func f _ r <- p, App (Fun g) ys <- universe r, arity f <= length ys, any hof ys ]
			lookupNodes = flip lookup (zip nodes [1..])
			arities = [ (f, length a) | Func f a _ <- p ] ++ map (flip (,) 2) primitives
			arity f = fromMaybe (error "Missing function definition") $ lookup f arities
			edges = nub . catMaybes $ [ if any hof ys then Nothing else Just ()
										>> liftM2 (\x y -> (x, y, "")) (lookupNodes f) (lookupNodes g)
										| Func f _ r <- p, App (Fun g) ys <- universe r, arity f <= length ys ]
			hof (App (Fun f) ys) = arity f > length ys
			hof (Fun f) = arity f > 0
			hof _ = False
	
	data TravArcType = Call | Composition
	
	travGraph :: Prog -> Gr Id TravArcType
	travGraph p = mkGraph (zip [1..] nodes) edges
		where
			edgesRaw = trav p
			nodes = nub . uncurry (++) . (\(_, x, y) -> (x, y)) . unzip3 $ edgesRaw
			lookupNodes = fromJust . flip lookup (zip nodes [1..])
			edges = [ (lookupNodes f, lookupNodes g, t) | (t@(Call), f, g) <- edgesRaw ]
	
	produceDotTrav :: Gr Id TravArcType -> String
	produceDotTrav g = printDotGraph $ graphToDot True g [] labelNode labelEdge
		where
			labelNode (_, l) = [Label (StrLabel l)] ++ if words l !! 0 `elem` primitives then [Style [SItem Dotted []]] else []
			labelEdge (_, _, t) = case t of { Call -> [] ; Composition -> [Style [SItem Dashed []], ArrowHead emptyArr] }
	
	trav :: Prog -> [(TravArcType, Id, Id)]
	trav p = fst $ execState (t [] "main" (body "main")) ([], [])
		where
			m = byFuncName (p ++ [ Func f [Var "_", Var "_"] Bottom | f <- primitives ])
			body f = fromMaybe (error "Missing function definition") $ Map.lookup f m >>= return . funcRhs
			arity = length . args
			args f = fromMaybe (error "Missing function definition") $ Map.lookup f m >>= return . concatMap patVars . funcArgs
			unsat (App (Fun f) ys) = arity f > length ys
			unsat (Fun f) = arity f > 0
			unsat _ = False
			t :: [(Id, Exp)] -> Id -> Exp -> State ([(TravArcType, Id, Id)], [Id]) Exp
			t env f (App (App x ys) zs) = t env f (App x $ ys ++ zs)
			t env f (Var v) = return $ fromMaybe (Con "") $ lookup v env
			t env f (App (Fun g) ys) | arity g <= length ys = do
										(oldEdges, oldNames) <- get
										put (oldEdges, [])
										bs <- sequence [ t env f e >>= \e' -> return $ if unsat e' then Just (v, e') else Nothing | (v, e) <- zip (args g) ys ]
										g' <- return . concat . intersperse " " $ g : (map (\x -> fromMaybe "_" (return . show . snd =<< x)) $ bs)
										(newEdges, newNames) <- get
										put ((Call, f, g'): map ((,,) Composition g') newNames ++ newEdges, [])
										e' <- if g' `notElem` [ x | (Call, _, x) <- newEdges]
											then t (catMaybes bs) g' (body g)
											else return Bottom
										(newerEdges, _) <- get
										put (newerEdges, g':oldNames)
										return e'
			t env f (App x ys) = do
							x' <- t env f x
							if x' == x || x' == Bottom then mapM (t env f) ys >>= return . App x' else t env f (App x' ys)
			t env f (Let bs x) = do
							bs' <- sequence [ liftM ((,) v) (t env f e) | (v, e) <- bs ]
							t (bs ++ env) f x
			t env f (Case x as) = do
							x <- t env f x
							ys <- sequence [ t (map (flip (,) Bottom) (patVars p) ++ env) f y | (p, y) <- as ]
							return $ head (ys ++ [x])
			t env f e = return e
	
	selectFuncs n p = nub $ selectFuncs2 n p ++ ["main"]
	
	selectFuncs1 :: Prog -> [Id]
	selectFuncs1 p = (evalState (traverse "main") Set.empty)
		where
			m = byFuncName p
			body f = fromMaybe Bottom (Map.lookup f m >>= return . funcRhs)
			traverse :: Id -> State (Set.Set Id) [Id]
			traverse f = let
				cs = filter (flip notElem $ f : primitives) . calls . body $ f
				csSet = Set.fromList cs in do
					visited <- get
					put $ csSet `Set.union` visited
					rest <- mapM traverse . Set.toList $ csSet `Set.difference` visited
					return $ if length cs > 1 then concat rest ++ [f] else concat rest
					
	selectFuncs2 :: Int -> Prog -> [Id]
	selectFuncs2 n p = take n [ f | (f, n) <- selected, n >= limit ]
		where
			selected :: [(Id, Int)]
			selected = map (\f -> maybe (f, -1) ((,) f) $ lookup f call_count) $ selectFuncs1 p
			call_count :: [(Id, Int)]
			call_count = map (\xs@(x:_) -> (x, length xs)) . group . sort . concatMap (calls . funcRhs) $ p
			limit :: Int
			limit | length selected > n = (reverse . sort . map snd $ selected) !! n
				  | otherwise			= 0