module LLVM.Analysis.CallGraphSCCTraversal (
callGraphSCCTraversal,
parallelCallGraphSCCTraversal,
FuncLike(..),
ComposableAnalysis,
Lens,
callGraphAnalysis,
callGraphAnalysisM,
callGraphComposeAnalysis,
composableAnalysis,
composableDependencyAnalysis,
composableAnalysisM,
composableDependencyAnalysisM,
) where
import Control.DeepSeq
import Control.Lens hiding (pre)
import Control.Monad ( foldM, replicateM )
import Control.Monad.Par.Scheds.Direct
import Data.List ( foldl' )
import Data.Map ( Map )
import qualified Data.Map as M
import Data.Maybe ( fromMaybe )
import Data.Monoid
import LLVM.Analysis
import LLVM.Analysis.CallGraph
import LLVM.Analysis.Types
import Data.Graph.Interface
import Data.Graph.MutableDigraph
import Data.Graph.Algorithms.Condense
import Data.Graph.Algorithms.DFS
type FunctionGraph = SparseDigraph Function ()
type SCCGraph = SparseDigraph [(Vertex, VertexLabel FunctionGraph)] ()
data ComposableAnalysis compSumm funcLike =
forall summary m . (NFData summary, Monoid summary, Eq summary, Monad m)
=> ComposableAnalysisM { analysisUnwrap :: m summary -> summary
, analysisFunctionM :: funcLike -> summary -> m summary
, summaryLens :: Simple Lens compSumm summary
}
| forall summary deps m . (NFData summary, Monoid summary, Eq summary, Monad m)
=> ComposableAnalysisDM { analysisUnwrap :: m summary -> summary
, analysisFunctionDM :: deps -> funcLike -> summary -> m summary
, summaryLens :: Simple Lens compSumm summary
, dependencyLens :: Simple Lens compSumm deps
}
| forall summary . (NFData summary, Monoid summary, Eq summary)
=> ComposableAnalysis { analysisFunction :: funcLike -> summary -> summary
, summaryLens :: Simple Lens compSumm summary
}
| forall summary deps . (NFData summary, Monoid summary, Eq summary)
=> ComposableAnalysisD { analysisFunctionD :: deps -> funcLike -> summary -> summary
, summaryLens :: Simple Lens compSumm summary
, dependencyLens :: Simple Lens compSumm deps
}
callGraphSCCTraversal :: (FuncLike funcLike)
=> CallGraph
-> ([funcLike] -> summary -> summary)
-> summary
-> summary
callGraphSCCTraversal callgraph f seed =
foldr applyAnalysis seed sccList
where
cg = definedCallGraph callgraph
sccList = topsort' cg
applyAnalysis component =
f (map (fromFunction . snd) component)
definedCallGraph :: CallGraph -> SCCGraph
definedCallGraph = condense . projectDefinedFunctions . callGraphRepr
parallelCallGraphSCCTraversal :: (NFData summary, Monoid summary, FuncLike funcLike)
=> CallGraph
-> ([funcLike] -> summary -> summary)
-> summary
-> summary
parallelCallGraphSCCTraversal callgraph f seed = runPar $ do
outputVars <- replicateM (numVertices cg) new
let sccs = labeledVertices cg
varMap = M.fromList (zip (map fst sccs) outputVars)
sccsWithVars = map (attachVars cg varMap) sccs
rootOutVars <- foldM (forkSCC f seed) [] (force sccsWithVars)
finalVals <- mapM get rootOutVars
return $! mconcat finalVals
where
cg = definedCallGraph callgraph
attachVars :: SCCGraph -> Map Int (IVar summary) -> (Vertex, VertexLabel SCCGraph)
-> ([Function], [IVar summary], IVar summary, Bool)
attachVars cg varMap (nid, component) =
(map snd component, inVars, outVar, isRoot)
where
outVar = varMap M.! nid
inVars = map (getDep varMap) deps
deps = filter (/=nid) $ suc cg nid
isRoot = null (pre cg nid)
forkSCC :: (NFData summary, Monoid summary, FuncLike funcLike)
=> ([funcLike] -> summary -> summary)
-> summary
-> [IVar summary]
-> ([Function], [IVar summary], IVar summary, Bool)
-> Par [IVar summary]
forkSCC f val0 acc (component, inVars, outVar, isRoot) = do
fork $ do
depVals <- mapM get inVars
let seed = case null inVars of
True -> val0
False -> force $ mconcat depVals
funcLikes = map fromFunction component
sccSummary = f funcLikes seed
put outVar sccSummary
case isRoot of
False -> return acc
True -> return (outVar : acc)
callGraphAnalysisM :: (FuncLike funcLike, Eq summary, Monad m)
=> (m summary -> summary)
-> (funcLike -> summary -> m summary)
-> ([funcLike] -> summary -> summary)
callGraphAnalysisM unwrap analyzeFunc = f
where
f [singleFunc] summ = unwrap $ analyzeFunc singleFunc summ
f funcs summ = unwrap $ go funcs summ
go funcs summ = do
newSumm <- foldM (flip analyzeFunc) summ funcs
case newSumm == summ of
True -> return summ
False -> go funcs newSumm
callGraphAnalysis :: (FuncLike funcLike, Eq summary)
=> (funcLike -> summary -> summary)
-> ([funcLike] -> summary -> summary)
callGraphAnalysis analyzeFunc = f
where
f [singleFunc] summ = analyzeFunc singleFunc summ
f funcs summ =
let newSumm = foldr analyzeFunc summ funcs
in case newSumm == summ of
True -> summ
False -> f funcs newSumm
callGraphComposeAnalysis :: (FuncLike funcLike, Monoid compSumm, Eq compSumm)
=> [ComposableAnalysis compSumm funcLike]
-> ([funcLike] -> compSumm -> compSumm)
callGraphComposeAnalysis analyses = f
where
f funcs summ =
foldl' (applyAnalysisN funcs) summ analyses
applyAnalysisN funcs summ a@ComposableAnalysisM { analysisUnwrap = unwrap
, analysisFunctionM = af
, summaryLens = lns
} =
let inputSummary = summ ^. lns
res = unwrap $ foldM (flip af) inputSummary funcs
in case res == inputSummary of
True -> summ
False -> applyAnalysisN funcs (set lns res summ) a
applyAnalysisN funcs summ a@ComposableAnalysisDM { analysisUnwrap = unwrap
, analysisFunctionDM = af
, summaryLens = lns
, dependencyLens = dlns
} =
let inputSummary = summ ^. lns
deps = summ ^. dlns
af' = af deps
res = unwrap $ foldM (flip af') inputSummary funcs
in case res == inputSummary of
True -> summ
False -> applyAnalysisN funcs (set lns res summ) a
applyAnalysisN funcs summ a@ComposableAnalysis { analysisFunction = af
, summaryLens = lns
} =
let inputSummary = summ ^. lns
res = foldr af inputSummary funcs
in case res == inputSummary of
True -> summ
False -> applyAnalysisN funcs (set lns res summ) a
applyAnalysisN funcs summ a@ComposableAnalysisD { analysisFunctionD = af
, summaryLens = lns
, dependencyLens = dlns
} =
let inputSummary = summ ^. lns
deps = summ ^. dlns
res = foldr (af deps) inputSummary funcs
in case res == inputSummary of
True -> summ
False -> applyAnalysisN funcs (set lns res summ) a
composableAnalysisM :: (NFData summary, Monoid summary, Eq summary, Monad m, FuncLike funcLike)
=> (m summary -> summary)
-> (funcLike -> summary -> m summary)
-> Simple Lens compSumm summary
-> ComposableAnalysis compSumm funcLike
composableAnalysisM = ComposableAnalysisM
composableDependencyAnalysisM :: (NFData summary, Monoid summary, Eq summary, Monad m, FuncLike funcLike)
=> (m summary -> summary)
-> (deps -> funcLike -> summary -> m summary)
-> Simple Lens compSumm summary
-> Simple Lens compSumm deps
-> ComposableAnalysis compSumm funcLike
composableDependencyAnalysisM = ComposableAnalysisDM
composableAnalysis :: (NFData summary, Monoid summary, Eq summary, FuncLike funcLike)
=> (funcLike -> summary -> summary)
-> Simple Lens compSumm summary
-> ComposableAnalysis compSumm funcLike
composableAnalysis = ComposableAnalysis
composableDependencyAnalysis :: (NFData summary, Monoid summary, Eq summary, FuncLike funcLike)
=> (deps -> funcLike -> summary -> summary)
-> Simple Lens compSumm summary
-> Simple Lens compSumm deps
-> ComposableAnalysis compSumm funcLike
composableDependencyAnalysis = ComposableAnalysisD
projectDefinedFunctions :: CG -> FunctionGraph
projectDefinedFunctions g = mkGraph ns' es'
where
es = edges g
ns = labeledVertices g
ns' = foldr keepDefinedFunctions [] ns
es' = map (\(Edge s d _) -> (Edge s d ())) $ filter (edgeIsBetweenDefined m) es
m = M.fromList ns
keepDefinedFunctions :: (Vertex, VertexLabel CG)
-> [(Vertex, VertexLabel FunctionGraph)]
-> [(Vertex, VertexLabel FunctionGraph)]
keepDefinedFunctions (nid, DefinedFunction f) acc = (nid, f) : acc
keepDefinedFunctions _ acc = acc
edgeIsBetweenDefined :: Map Int CallNode -> Edge CG -> Bool
edgeIsBetweenDefined m (Edge src dst _) =
nodeIsDefined m src && nodeIsDefined m dst
nodeIsDefined :: Map Int CallNode -> Int -> Bool
nodeIsDefined m n =
case M.lookup n m of
Just (DefinedFunction _) -> True
_ -> False
getDep :: Map Int c -> Int -> c
getDep m n = fromMaybe errMsg (M.lookup n m)
where
errMsg = error ("LLVM.Analysis.CallGraphSCCTraversal.getDep: Missing expected output var for node: " ++ show n)