module LLVM.Analysis.CFG (
CFG(..),
RCFG(..),
CFGEdge(..),
CFGType,
HasCFG(..),
mkCFG,
reverseCFG,
basicBlockPredecessors,
basicBlockSuccessors,
basicBlockPredecessorEdges,
basicBlockSuccessorEdges,
basicBlockLabeledPredecessors,
basicBlockLabeledSuccessors,
instructionReachable,
cfgGraphvizRepr
) where
import Control.Arrow ( first )
import Data.GraphViz
import Data.List ( foldl' )
import Data.Maybe ( fromMaybe )
import Text.Printf
import Data.Graph.Interface
import Data.Graph.MutableDigraph
import Data.Graph.Algorithms.Basic
import LLVM.Analysis
import LLVM.Analysis.Types
type CFGType = DenseDigraph Instruction CFGEdge
type LEdgeType = Edge CFGType
type NodeType = Vertex
type LNodeType = (Vertex, VertexLabel CFGType)
instance Labellable CFGEdge where
toLabelValue = toLabelValue . show
data CFG = CFG { cfgGraph :: CFGType
, cfgEntryValue :: Instruction
, cfgEntryNode :: NodeType
, cfgExitValue :: Instruction
, cfgExitNode :: NodeType
, cfgFunction :: Function
}
data RCFG = RCFG { rcfgGraph :: CFGType
, rcfgEntryValue :: Instruction
, rcfgEntryNode :: NodeType
, rcfgExitValue :: Instruction
, rcfgExitNode :: NodeType
, rcfgFunction :: Function
}
data CFGEdge =
UnconditionalEdge
| DefaultEdge
| TrueEdge Value
| FalseEdge Value
| EqualityEdge Value Value
| IndirectEdge Value
| NormalEdge Instruction
| UnwindEdge Instruction
deriving (Ord, Eq)
instance Show CFGEdge where
show UnconditionalEdge = ""
show DefaultEdge = "<default>"
show (TrueEdge v) = printf "[%s] is true" (show v)
show (FalseEdge v) = printf "[%s] is false" (show v)
show (EqualityEdge v1 v2) = printf "[%s] is [%s]" (show v1) (show v2)
show (IndirectEdge v) = printf "[%s] (indirect)" (show v)
show (NormalEdge i) = printf "[%s] (invoke normal)" (show i)
show (UnwindEdge i) = printf "[%s] (invoke unwind)" (show i)
class HasCFG a where
getCFG :: a -> CFG
instance HasCFG CFG where
getCFG = id
instance HasCFG Function where
getCFG = mkCFG
instance HasFunction CFG where
getFunction = cfgFunction
instance HasFunction RCFG where
getFunction = rcfgFunction
instance FuncLike CFG where
fromFunction = mkCFG
mkCFG :: Function -> CFG
mkCFG func = CFG { cfgGraph = g
, cfgFunction = func
, cfgEntryValue = entryVal
, cfgEntryNode = instructionUniqueId entryVal
, cfgExitValue = exitVal
, cfgExitNode = instructionUniqueId exitVal
}
where
entryVal = functionEntryInstruction func
Just exitVal = functionExitInstruction func
g = mkGraph cfgNodes (concat cfgEdges)
(cfgNodes, cfgEdges) = foldl' buildBlockGraph ([], []) (functionBody func)
reverseCFG :: CFG -> RCFG
reverseCFG g = RCFG { rcfgGraph = grev (cfgGraph g)
, rcfgFunction = cfgFunction g
, rcfgEntryValue = cfgExitValue g
, rcfgEntryNode = cfgExitNode g
, rcfgExitValue = cfgEntryValue g
, rcfgExitNode = cfgEntryNode g
}
toInternalEdge :: (Instruction, Instruction) -> LEdgeType
toInternalEdge (s, d) = Edge sid did UnconditionalEdge
where
sid = instructionUniqueId s
did = instructionUniqueId d
buildBlockGraph :: ([LNodeType], [[LEdgeType]]) -> BasicBlock -> ([LNodeType], [[LEdgeType]])
buildBlockGraph (nacc, eacc) bb = (newNodes ++ nacc, (termEdges ++ internalEdges) : eacc)
where
blockInsts = basicBlockInstructions bb
newNodes = map (\i -> (instructionUniqueId i, i)) blockInsts
termInst = basicBlockTerminatorInstruction bb
termNodeId = instructionUniqueId termInst
otherInsts = filter (/= termInst) blockInsts
internalEdgePairings = case null otherInsts of
True -> []
False -> zip blockInsts (tail blockInsts)
internalEdges = map toInternalEdge internalEdgePairings
termEdges = case termInst of
RetInst {} -> []
UnconditionalBranchInst { unconditionalBranchTarget = tgt } ->
[ Edge termNodeId (jumpTargetId tgt) UnconditionalEdge ]
BranchInst { branchCondition = cond
, branchTrueTarget = tTarget
, branchFalseTarget = fTarget
} ->
[ Edge termNodeId (jumpTargetId tTarget) (TrueEdge cond)
, Edge termNodeId (jumpTargetId fTarget) (FalseEdge cond)
]
SwitchInst { switchValue = cond
, switchDefaultTarget = defTarget
, switchCases = cases
} ->
Edge termNodeId (jumpTargetId defTarget) DefaultEdge :
map (caseEdge termNodeId cond) cases
IndirectBranchInst { indirectBranchAddress = addr
, indirectBranchTargets = targets
} ->
map (indirectEdge termNodeId addr) targets
InvokeInst { invokeNormalLabel = n
, invokeUnwindLabel = u
} ->
[ Edge termNodeId (jumpTargetId n) (NormalEdge termInst)
, Edge termNodeId (jumpTargetId u) (UnwindEdge termInst)
]
UnreachableInst {} -> []
ResumeInst {} -> []
_ -> error ("LLVM.Analysis.CFG.buildBlockGraph: Last instruction in a block should be a terminator: " ++ show (toValue termInst))
jumpTargetId :: BasicBlock -> Int
jumpTargetId bb = instructionUniqueId t
where
(t:_) = basicBlockInstructions bb
caseEdge :: NodeType -> Value -> (Value, BasicBlock) -> LEdgeType
caseEdge thisNodeId cond (val, dest) =
Edge thisNodeId (jumpTargetId dest) (EqualityEdge cond val)
indirectEdge :: NodeType -> Value -> BasicBlock -> LEdgeType
indirectEdge thisNodeId addr target =
Edge thisNodeId (jumpTargetId target) (IndirectEdge addr)
toBlock :: CFGType -> NodeType -> BasicBlock
toBlock cfg n =
case lab cfg n of
Nothing -> error ("LLVM.Analysis.CFG.toBlock: Instruction missing from CFG: " ++ show n)
Just i ->
let errMsg = error ("LLVM.Analysis.CFG.toBlock: Instruction in CFG should have a basic block: " ++ show i)
in fromMaybe errMsg (instructionBasicBlock i)
basicBlockPredecessors :: CFG -> BasicBlock -> [BasicBlock]
basicBlockPredecessors cfg bb = map (toBlock cfg') ps
where
cfg' = cfgGraph cfg
firstInst : _ = basicBlockInstructions bb
ps = pre cfg' (instructionUniqueId firstInst)
basicBlockSuccessors :: CFG -> BasicBlock -> [BasicBlock]
basicBlockSuccessors cfg bb = map (toBlock cfg') ss
where
cfg' = cfgGraph cfg
exitInst = basicBlockTerminatorInstruction bb
ss = suc cfg' (instructionUniqueId exitInst)
basicBlockPredecessorEdges :: CFG -> BasicBlock -> [CFGEdge]
basicBlockPredecessorEdges cfg bb =
map edgeLabel $ inn (cfgGraph cfg) (instructionUniqueId startInst)
where
startInst : _ = basicBlockInstructions bb
basicBlockSuccessorEdges :: CFG -> BasicBlock -> [CFGEdge]
basicBlockSuccessorEdges cfg bb =
map edgeLabel $ out (cfgGraph cfg) (instructionUniqueId exitInst)
where
exitInst = basicBlockTerminatorInstruction bb
basicBlockLabeledSuccessors :: CFG -> BasicBlock -> [(BasicBlock, CFGEdge)]
basicBlockLabeledSuccessors cfg bb =
map (first (toBlock cfg')) ss
where
cfg' = cfgGraph cfg
exitInst = basicBlockTerminatorInstruction bb
ss = lsuc cfg' (instructionUniqueId exitInst)
basicBlockLabeledPredecessors :: CFG -> BasicBlock -> [(BasicBlock, CFGEdge)]
basicBlockLabeledPredecessors cfg bb =
map (first (toBlock cfg')) ps
where
cfg' = cfgGraph cfg
startInst : _ = basicBlockInstructions bb
ps = lpre cfg' (instructionUniqueId startInst)
instructionReachable :: CFG -> Instruction -> Bool
instructionReachable cfg i =
case null (basicBlockPredecessors cfg bb) of
True -> bb == firstBlock
False -> True
where
Just bb = instructionBasicBlock i
f = basicBlockFunction bb
firstBlock : _ = functionBody f
cfgGraphvizParams :: GraphvizParams n Instruction CFGEdge BasicBlock Instruction
cfgGraphvizParams =
defaultParams { fmtNode = \(_,l) -> [toLabel (toValue l)]
, fmtEdge = formatEdge
, clusterID = Int . basicBlockUniqueId
, fmtCluster = formatCluster
, clusterBy = nodeCluster
}
where
nodeCluster l@(_, i) =
let Just bb = instructionBasicBlock i
in C bb (N l)
formatCluster bb = [GraphAttrs [toLabel (show (basicBlockName bb))]]
formatEdge (_, _, l) =
let lbl = toLabel l
in case l of
TrueEdge _ -> [lbl, color ForestGreen]
FalseEdge _ -> [lbl, color Crimson]
EqualityEdge _ _ -> [lbl, color DeepSkyBlue]
IndirectEdge _ -> [lbl, color Indigo, style dashed]
UnwindEdge _ -> [lbl, color Tomato4, style dotted]
_ -> [lbl]
cfgGraphvizRepr :: CFG -> DotGraph NodeType
cfgGraphvizRepr cfg = graphElemsToDot cfgGraphvizParams ns es
where
g = cfgGraph cfg
ns = labeledVertices g
es = map (\(Edge s d l) -> (s, d, l)) (edges g)