futhark-0.15.4: src/Futhark/Analysis/CallGraph.hs
-- | This module exports functionality for generating a call graph of
-- an Futhark program.
module Futhark.Analysis.CallGraph
( CallGraph
, buildCallGraph
, isFunInCallGraph
, calls
, calledByConsts
, allCalledBy
)
where
import Control.Monad.Writer.Strict
import qualified Data.Map.Strict as M
import qualified Data.Set as S
import Data.Maybe (fromMaybe)
import Data.List (foldl')
import Futhark.Representation.SOACS
type FunctionTable = M.Map Name (FunDef SOACS)
buildFunctionTable :: Prog SOACS -> FunctionTable
buildFunctionTable = foldl expand M.empty . progFuns
where expand ftab f = M.insert (funDefName f) f ftab
type FunGraph = M.Map Name (S.Set Name)
-- | The call graph is a mapping from a function name, i.e., the
-- caller, to a set of the names of functions called *directly* (not
-- transitively!) by the function.
--
-- We keep track separately of the functions called by constants.
data CallGraph = CallGraph { calledByFuns :: M.Map Name (S.Set Name)
, calledInConsts :: S.Set Name
}
-- | Is the given function known to the call graph?
isFunInCallGraph :: Name -> CallGraph -> Bool
isFunInCallGraph f = M.member f . calledByFuns
-- | Does the first function call the second?
calls :: Name -> Name -> CallGraph -> Bool
calls caller callee =
maybe False (S.member callee) . M.lookup caller . calledByFuns
-- | Is the function called in any of the constants?
calledByConsts :: Name -> CallGraph -> Bool
calledByConsts f = S.member f . calledInConsts
-- | All functions called by this function.
allCalledBy :: Name -> CallGraph -> S.Set Name
allCalledBy f = fromMaybe mempty . M.lookup f . calledByFuns
-- | @buildCallGraph prog@ build the program's call graph.
buildCallGraph :: Prog SOACS -> CallGraph
buildCallGraph prog =
CallGraph fg $ buildFGStms $ progConsts prog
where fg = foldl' (buildFGfun ftable) M.empty entry_points
entry_points = map funDefName $ progFuns prog
ftable = buildFunctionTable prog
-- | @buildCallGraph ftable fg fname@ updates @fg@ with the
-- contributions of function @fname@.
buildFGfun :: FunctionTable -> FunGraph -> Name -> FunGraph
buildFGfun ftable fg fname =
-- Check if function is a non-builtin that we have not already
-- processed.
case M.lookup fname ftable of
Just f | Nothing <- M.lookup fname fg -> do
let callees = buildFGBody $ funDefBody f
fg' = M.insert fname callees fg
-- recursively build the callees
foldl' (buildFGfun ftable) fg' callees
_ -> fg
buildFGStms :: Stms SOACS -> S.Set Name
buildFGStms = mconcat . map (buildFGexp . stmExp) . stmsToList
buildFGBody :: Body -> S.Set Name
buildFGBody = buildFGStms . bodyStms
buildFGexp :: Exp -> S.Set Name
buildFGexp (Apply fname _ _ _) = S.singleton fname
buildFGexp (Op op) = execWriter $ mapSOACM folder op
where folder = identitySOACMapper {
mapOnSOACLambda = \lam -> do tell $ buildFGBody $ lambdaBody lam
return lam
}
buildFGexp e = execWriter $ mapExpM folder e
where folder = identityMapper {
mapOnBody = \_ body -> do tell $ buildFGBody body
return body
}