-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathIFDS.hs
624 lines (545 loc) · 24.9 KB
/
IFDS.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
{-# LANGUAGE MultiParamTypeClasses, TemplateHaskell #-}
{-# OPTIONS_GHC -funbox-strict-fields #-}
-- | This is an implementation of the IFDS algorithm as defined by RHS
-- in
--
-- > Precise Interprocedural Dataflow Analysis via Graph Reachability (POPL 1995)
-- > doi: http://doi.acm.org/10.1145/199448.199462
--
-- The algorithm uses dynamic programming to solve some
-- interprocedural dataflow analysis problems precisely, finding the
-- meet-over-all-valid-paths solution (instead of the
-- meet-over-all-paths solution, which includes data flow across
-- unmatched call/return pairs). It solves a class of problems called
-- the Interprocedural Finite Distributive Subset problems in cubic
-- time; a larger class of problems is addressed by the closely
-- related IDE framework.
--
-- The framework solves _subset_ problems; elements of this set are
-- drawn from a _domain_ @d@. To solve a problem, the algorithm takes
-- the interprocedural control flow graph (called the supergraph in
-- RHS95) and converts it into an _exploded supergraph_ where each
-- node in the original ICFG is replaced by a node for _each_ possible
-- element in @d@. Edges are drawn between elements in @d@ according
-- to a set of flow functions (intra-procedural and inter-procedural).
-- RHS95 refers to the flow functions and the edges they induce
-- between two statements as the _representation relation_.
--
-- Some of the efficiency of the algorithm is derived from this
-- "point-wise" treatment of the domain.
--
-- This particular implementation uses the formulation of Naeem et al
-- in
--
-- > Practical Extensions to the IFDS Algorithm (CC 2010)
-- > doi: http://dx.doi.org/10.1007/978-3-642-11970-5_8
--
-- These extensions make the algorithm practical for larger programs.
-- In particular, this formulation avoids constructing the entire
-- exploded supergraph G#; instead, it implicitly constructs only the
-- reachable portion of the graph.
module LLVM.Analysis.IFDS (
-- * Types
IFDSAnalysis(..),
IFDSResult,
-- * Entry point
ifds,
-- * Accessors
ifdsInstructionResult,
ifdsResultEdgeCount
) where
import Control.Monad.State
import Data.Graph.Inductive hiding ( (><) )
import Data.Map ( Map )
import Data.Set ( Set )
import qualified Data.Map as M
import qualified Data.Set as S
import Debug.Trace.LocationTH
import Text.Printf
import LLVM.Analysis
import LLVM.Analysis.CFG
import LLVM.Analysis.ICFG
import LLVM.Analysis.Internal.Worklist
-- | The interface to define an IFDS analysis. There are variants of
-- the interprocedural flow functions to handle /external/ functions.
-- Known references are provided for known external functions.
-- Unknown functions can be called in Modules that do not have a
-- 'main' function or when they contain calls to dlopen.
class IFDSAnalysis a domType where
flow :: a -> Maybe domType -> Instruction -> [CFGEdge] -> [Maybe domType]
-- ^ Compute local flow information for the domain element after this
-- instruction is executed. The returned list is the list of domain
-- variables reachable from this domain element after the statement is
-- executed. This models local control flow.
callFlow :: a -> Maybe domType -> Instruction -> [CFGEdge] -> [Maybe domType]
-- ^ Similar to 'flow', but models local information flow across
-- call->return edges.
-- | Similar to 'flow', but models flow from return nodes to their
-- successors. The default implementation should be sufficient for
-- most purposes
returnNodeFlow :: a -> Maybe domType -> Instruction -> [Maybe domType]
returnNodeFlow _ v _ = [v]
passArgs :: a -> Maybe domType -> Instruction -> Function -> [Maybe domType]
-- ^ Pass information from a call to the entry of the callee
externPassArgs :: a -> Maybe domType -> Instruction -> Maybe ExternalFunction -> [Maybe domType]
-- ^ Similar to 'passArgs', but for external (possibly unknown)
-- functions. The 'ExternalFunction' is @Nothing@ if the external
-- function is unknown.
returnVal :: a -> Maybe domType -> Instruction -> Instruction -> [Maybe domType]
-- ^ Pass information about globals and the actual return value back
-- to the caller.
externReturnVal :: a -> Maybe domType -> Maybe ExternalFunction -> Instruction -> [Maybe domType]
-- ^ 'retVal' for external functions. The external function is
-- 'Nothing' when the return is from an unknown external function.
entrySetup :: a -> Module -> Function -> [Maybe domType]
-- ^ Add domain elements to the initial set of PathEdges.
-- | An edge from <s_p, d_1> to <n, d_2> noting that <n, d_2> is
-- reachable in the exploded supergraph from <s_p, d_1>. s_p is not
-- explicitly recorded because it is uniquely determined by n.
data PathEdge domType = PathEdge !(Maybe domType) !Node !(Maybe domType)
deriving (Ord, Eq, Show)
-- After the analysis is done, reduce the Set PathEdge -> Map (Node,
-- domType) [domType] to start answering queries. Really, only the
-- second two members are useful (reachable vs not reachable).
-- | An edge summarizing the flow information for the call site
-- identified by the node.
data SummaryEdge domType = SummaryEdge !Node !(Maybe domType) !(Maybe domType)
deriving (Ord, Eq, Show)
data IFDSNode domType = IFDSNode !Node !(Maybe domType)
deriving (Ord, Eq, Show)
-- | The current state of the IFDS analysis. It includes the PathEdge
-- and SummaryEdge sets, as well as a few important caches. It also
-- maintains the worklist. There is a reference to the ICFG being
-- analyzed for convenience.
data IFDS a domType = IFDS { pathEdges :: Set (PathEdge domType)
-- ^ The PathEdge set from the algorithm
, summaryEdges :: Set (SummaryEdge domType)
-- ^ The SummaryEdge set from the algorithm
, incomingNodes :: Map (IFDSNode domType) (Set (IFDSNode domType))
-- ^ A reverse mapping. This maps G#
-- (exploded supergraph) nodes
-- corresponding to function entries to the
-- calls that induce them.
, endSummary :: Map (IFDSNode domType) (Set (IFDSNode domType))
, entryValues :: Map Node (Set (Maybe domType))
-- ^ A cache of domain elements reachable
-- at the entry node of a procedure (the
-- key in the map is the call node that is
-- reachable by the entries in the set).
, summaryValues :: Map Node (Set (Maybe domType))
-- ^ A cache of domain elements at the
-- target of a summary edge. This lets us
-- implement the second query at line 17
-- efficiently
, ifdsWorklist :: Worklist (PathEdge domType)
-- ^ A simple worklist
, icfg :: ICFG
-- ^ The ICFG that this analysis is operating on
, ifdsAnalysis :: a
}
type IFDSM a domType r = State (IFDS a domType) r
-- | An opaque wrapper around the results of an IFDS analysis. Use
-- 'ifdsInstructionResult' to extract information.
data IFDSResult domType = IFDSResult Int (Map Instruction (Set domType))
instance (Show domType, Ord domType) => Show (IFDSResult domType) where
show = showIFDSResult
showIFDSResult :: (Show domType, Ord domType) => IFDSResult domType -> String
showIFDSResult (IFDSResult _ r) = unlines $ map showProgramPoint $ M.toList r
where
showProgramPoint (inst, s) =
let memberStrings = S.toList $ S.map showMember s
Just bb = instructionBasicBlock inst
f = basicBlockFunction bb
in concat $ printf "%s: (%d) %s" (show (functionName f)) (instructionUniqueId inst) (show inst) : "\n" : memberStrings
showMember m = " " ++ show m ++ "\n"
-- | Extract the set of values that are reachable from some entry
-- point at the given 'Instruction'. If the Instruction is not in the
-- Module, returns Nothing.
ifdsInstructionResult :: IFDSResult domType -> Instruction -> Maybe (Set domType)
ifdsInstructionResult (IFDSResult _ m) i = M.lookup i m
-- | Return the number of edges in the reachable portion of the
-- exploded subgraph. This is mostly useful for regression testing to
-- ensure that there aren't too many edges introduced.
ifdsResultEdgeCount :: IFDSResult domType -> Int
ifdsResultEdgeCount (IFDSResult c _) = c
-- | Run the IFDS analysis on the given ICFG. Currently it is forward
-- only. Support for backwards analysis could be added somewhat
-- easily.
ifds :: (IFDSAnalysis a domType, Ord domType, Show domType) => a -> ICFG -> IFDSResult domType
ifds analysis g = extractSolution finalState
where
initialEdges = concatMap (mkInitialEdges analysis (icfgModule g)) (icfgEntryPoints g)
initialState = IFDS { pathEdges = S.fromList initialEdges
, summaryEdges = S.empty
, ifdsWorklist = worklistFromList initialEdges
, incomingNodes = M.empty
, endSummary = M.empty
, entryValues = M.empty
, summaryValues = M.empty
, icfg = g
, ifdsAnalysis = analysis
}
finalState = execState tabulate initialState
-- | Given the final state from the tabulation algorithm, extract a
-- whole-program solution from it
extractSolution :: (IFDSAnalysis a domType, Ord domType, Show domType)
=> IFDS a domType -> IFDSResult domType
extractSolution s = IFDSResult (S.size ps) $ S.fold populateSolution M.empty ps
where
ps = pathEdges s
g = (icfgGraph . icfg) s
populateSolution (PathEdge _ n (Just d2)) m =
case nodeToInstruction n of
Nothing -> m
Just inst ->
let newSet = case M.lookup inst m of
Nothing -> S.singleton d2
Just vals -> S.insert d2 vals
in M.insert inst newSet m
populateSolution _ m = m
nodeToInstruction n = case l of
InstNode i -> Just i
_ -> Nothing
where
Just l = lab g n
-- | Grab the underlying ICFG graph
getICFG :: IFDSM a domType ICFGGraphType
getICFG = do
g <- gets icfg
return (icfgGraph g)
-- | The key function that builds up the PathEdge set using a worklist
-- algorithm. It handles the three cases outlined in the main
-- algorithm: adding interprocedural edges for call/invoke nodes,
-- adding interprocedural (and summary) edges for return nodes, and
-- adding intraprocedural edges for all other instructions.
tabulate :: (IFDSAnalysis a domType, Ord domType, Show domType) => IFDSM a domType ()
tabulate = do
worklist <- gets ifdsWorklist
case takeWorkItem worklist of
EmptyWorklist -> return () -- Done
e@(PathEdge _{-d1-} n _{-d2-}) :< rest -> do
modify (\s -> s { ifdsWorklist = rest })
g <- getICFG
let Just lbl = lab g n
case lbl of
-- Case 1: Calls
InstNode ci@CallInst {} -> addCallEdges ci e
InstNode ii@InvokeInst {} -> addCallEdges ii e
-- Case 2: Returns (including from externs)
InstNode ri@RetInst {} -> addExitEdges (Right ri) e
ExternalNode ef -> addExitEdges (Left ef) e
-- Case 3: intraprocedural control flow
InstNode i -> addIntraEdges i e
ReturnNode i -> addReturnNodeEdges i e
tabulate
-- | Get the list of domain values reachable from 'Node' @n@. This
-- request is always issued when we know that a particular domain
-- value @d1@ is reachable, so add that to the set of reachable values
-- before returning it.
updateCallReachingValues :: (Ord domType)
=> Node
-> Maybe domType
-> IFDSM a domType ()
updateCallReachingValues n d1 = do
evs <- gets entryValues
let updatedValues = maybe (S.singleton d1) (S.insert d1) (M.lookup n evs)
modify (\s -> s { entryValues = M.insert n updatedValues evs})
{-# INLINE updateCallReachingValues #-}
-- | Handle adding interprocedural call edges and intraprocedural
-- call->return edges.
addCallEdges :: (IFDSAnalysis a domType, Ord domType, Show domType)
=> Instruction
-> PathEdge domType
-> IFDSM a domType ()
addCallEdges ci e@(PathEdge d1 n _) = do
g <- gets icfg
-- Update the entryValues cache here since we know that d1 reaches
-- the call here.
updateCallReachingValues n d1
let possibleCalleeEntryNodes = getICFGCallEntries g n
mapM_ (addInfoForPotentialCallee ci e) possibleCalleeEntryNodes
{-# INLINE addCallEdges #-}
-- | Add call edges and intraprocedural call->return edges for a
-- single callee of a call site. The original algorithm assumes that
-- each call site has a single callee. This helper just handles all
-- possible callees.
addInfoForPotentialCallee :: (IFDSAnalysis a domType, Ord domType, Show domType)
=> Instruction
-> PathEdge domType
-> Node
-> IFDSM a domType ()
addInfoForPotentialCallee ci e@(PathEdge _ n d2) calleeEntry = do
g <- getICFG
analysis <- gets ifdsAnalysis
let Just calleeEntryLabel = lab g calleeEntry
-- Call passArgs to figure out how to map domain values in the
-- caller to the callee
let argToFormalEdges = case calleeEntryLabel of
InstNode entryInst -> passArgs analysis d2 ci (instructionFunction entryInst)
ExternalNode ef -> externPassArgs analysis d2 ci ef
_ -> $failure ("Expected InstNode or ExternalNode: " ++ show calleeEntryLabel)
-- This covers lines 14-16 of the algorithm (the call to passArgs
-- defines argToFormalEntries)
mapM_ (addInterProcAndSummaryEdges ci calleeEntry e) argToFormalEdges
-- The rest of this covers lines 17-19
summEdgeD3s <- getSummaryEdgeD3s n d2
let intraPredecessorEdges = map toIntraEdge $ lpre g n
callFlowD3s = callFlow analysis d2 ci intraPredecessorEdges
d3s = summEdgeD3s ++ callFlowD3s
mapM_ (extendCallToReturn e) d3s
{-# INLINE addInfoForPotentialCallee #-}
-- | Line 18
extendCallToReturn :: (Ord domType) => PathEdge domType -> Maybe domType -> IFDSM a domType ()
extendCallToReturn (PathEdge d1 n _) d3 =
propagate (PathEdge d1 retNode d3)
where
retNode = callNodeToReturnNode n
{-# INLINE extendCallToReturn #-}
-- | Part of the query on line 17
getSummaryEdgeD3s :: (Ord domType, Show domType)
=> Node
-> Maybe domType
-> IFDSM a domType [Maybe domType]
getSummaryEdgeD3s n d2 = do
summVals <- gets summaryValues
summEdges <- gets summaryEdges
let possibleD3s = maybe S.empty id (M.lookup n summVals)
return $ filter (isInSummaryEdge summEdges) (S.toList possibleD3s)
where
isInSummaryEdge summEdges d3 =
let summEdge = SummaryEdge n d2 d3
in S.member summEdge summEdges
{-# INLINE getSummaryEdgeD3s #-}
-- | Inner loop on lines 15-16
addInterProcAndSummaryEdges :: (IFDSAnalysis a domType, Ord domType, Show domType)
=> Instruction
-> Node
-> PathEdge domType
-> Maybe domType
-> IFDSM a domType ()
addInterProcAndSummaryEdges ci calleeEntry e@(PathEdge _ n d2) argEdgeD3 = do
endSummaries <- gets endSummary
let loop = PathEdge argEdgeD3 calleeEntry argEdgeD3
entryNode = IFDSNode calleeEntry argEdgeD3
callNode = IFDSNode n d2
callerExits = maybe S.empty id (M.lookup entryNode endSummaries)
-- Mark the entry of the function as reachable by element argEdgeD3
-- (derived from passArgs(d2) above)
propagate loop
addIncomingNode entryNode callNode
mapM_ (addCallSummaries ci e) (S.toList callerExits)
{-# INLINE addInterProcAndSummaryEdges #-}
-- | Handle adding edges for function call instructions (and invokes).
-- This function covers lines 15.3-15.5 in the algorithm from Naeem et al
addCallSummaries :: (IFDSAnalysis a domType, Ord domType, Show domType)
=> Instruction
-> PathEdge domType
-> IFDSNode domType
-> IFDSM a domType ()
addCallSummaries ci (PathEdge _ n d2) (IFDSNode e_p d4) = do
g <- getICFG
analysis <- gets ifdsAnalysis
let Just exitNode = lab g e_p
returnedVals = case exitNode of
InstNode retInst -> returnVal analysis d4 retInst ci
ExternalNode ef -> externReturnVal analysis d4 ef ci
_ -> $failure ("Expected InstNode or ExternalNode: " ++ show exitNode)
summEdges = map (\d5 -> SummaryEdge n d2 d5) returnedVals
mapM_ addSummaryEdge summEdges
{-# INLINE addCallSummaries #-}
addIncomingNode :: (Ord domType) => IFDSNode domType -> IFDSNode domType -> IFDSM a domType ()
addIncomingNode entryNode callNode = do
s <- get
let currentNodes = incomingNodes s
updatedNodes =
maybe (S.singleton callNode) (S.insert callNode) (M.lookup entryNode currentNodes)
put s { incomingNodes = M.insert entryNode updatedNodes currentNodes }
{-# INLINE addIncomingNode #-}
-- | Given a call node in the ICFG, get all of the entry nodes for its
-- possible targets. For a call to a defined function, this is the
-- first instruction in the function. For calls to external
-- functions, this just returns the node representing the external.
getICFGCallEntries :: ICFG -> Node -> [Node]
getICFGCallEntries g callNode =
map fst $ filter (isCallToEntry . snd) $ lsuc (icfgGraph g) callNode
{-# INLINE getICFGCallEntries #-}
isCallToEntry :: ICFGEdge -> Bool
isCallToEntry (CallToEntry _) = True
isCallToEntry _ = False
-- | Handle case 2 of the algorithm: function exit nodes (e_p, d_2).
-- This function adds a summary edge in the caller and adds propagates
-- local information in the caller along call-to-return edges.
--
-- A lot of the work actually happens in 'summarizeCallEdge', which is
-- broken out just because it was getting large. Hopefully the
-- inliner will take care of pasting everything back together for
-- additional optimization.
--
-- Note: n is e_p in the algorithm
addExitEdges :: (IFDSAnalysis a domType, Ord domType, Show domType)
=> Either (Maybe ExternalFunction) Instruction
-> PathEdge domType
-> IFDSM a domType ()
addExitEdges (Left ef) (PathEdge d1 n d2) = do
unknownId <- gets (icfgUnknownNode . icfg)
analysis <- gets ifdsAnalysis
incNodes <- gets incomingNodes
let e_p = n
s_p = case ef of
Nothing ->
let Just uid = unknownId
in uid
Just ef' -> externalFunctionUniqueId ef'
funcEntry = IFDSNode s_p d1
funcExit = IFDSNode e_p d2
retEdgeF = externReturnVal analysis d2 ef
callEdges = maybe S.empty id $ M.lookup funcEntry incNodes
addEndSummary funcEntry funcExit
mapM_ (summarizeCallEdge retEdgeF) (S.toList callEdges)
addExitEdges (Right ri) (PathEdge d1 n d2) = do
analysis <- gets ifdsAnalysis
incNodes <- gets incomingNodes
let e_p = n
s_p <- nodeToFunctionEntryNode e_p
let funcEntry = IFDSNode s_p d1
funcExit = IFDSNode e_p d2
retEdgeF = returnVal analysis d2 ri
callEdges = maybe S.empty id $ M.lookup funcEntry incNodes
addEndSummary funcEntry funcExit
mapM_ (summarizeCallEdge retEdgeF) (S.toList callEdges)
{-# INLINE addExitEdges #-}
summarizeCallEdge :: (Ord domType, Show domType)
=> (Instruction -> [Maybe domType])
-> IFDSNode domType
-> IFDSM a domType ()
summarizeCallEdge retEdgeF (IFDSNode c d4)= do
g <- getICFG
let Just (InstNode callInst) = lab g c
retEdges = retEdgeF callInst
mapM_ mkSummaryAndPathEdges retEdges
where
mkSummaryAndPathEdges d5 = do
summEdges <- gets summaryEdges
let summEdge = SummaryEdge c d4 d5
case summEdge `S.member` summEdges of
True -> return ()
False -> do
addSummaryEdge summEdge
entValMap <- gets entryValues
let entVals = maybe S.empty id (M.lookup c entValMap)
mapM_ (addCallToReturns d5) (S.toList entVals)
addCallToReturns d5 d3 = do
pedges <- gets pathEdges
let e1 = PathEdge d3 c d4
callToReturnEdge = PathEdge d3 (callNodeToReturnNode c) d5
case e1 `S.member` pedges of
False -> return () -- This edge isn't actually in G#
True -> propagate callToReturnEdge
{-# INLINE summarizeCallEdge #-}
addEndSummary :: (Ord domType) => IFDSNode domType -> IFDSNode domType -> IFDSM a domType ()
addEndSummary funcEntry funcExit = do
curSummaries <- gets endSummary
let updatedSummaries =
maybe (S.singleton funcExit) (S.insert funcExit) (M.lookup funcEntry curSummaries)
modify (\s -> s { endSummary = M.insert funcEntry updatedSummaries curSummaries })
{-# INLINE addEndSummary #-}
callNodeToReturnNode :: Node -> Node
callNodeToReturnNode = negate
{-# INLINE callNodeToReturnNode #-}
-- | Given a node in the ICFG corresponding to some instruction, find
-- the entry node for the function containing it.
-- nodeToFunctionEntryNode :: ICFG -> Node -> Node
nodeToFunctionEntryNode :: Node -> IFDSM a domType Node
nodeToFunctionEntryNode n = do
g <- getICFG
let Just nodLab = lab g n
InstNode i = nodLab
Just bb = instructionBasicBlock i
f = basicBlockFunction bb
s = functionEntryInstruction f
return (instructionUniqueId s)
{-# INLINE nodeToFunctionEntryNode #-}
instructionFunction :: Instruction -> Function
instructionFunction i = basicBlockFunction bb
where
Just bb = instructionBasicBlock i
{-# INLINE instructionFunction #-}
-- | Handle the case of local control flow (extending the
-- intraprocedural part of the exploded supergraph).
addIntraEdges :: (IFDSAnalysis a domType, Ord domType, Show domType)
=> Instruction
-> PathEdge domType
-> IFDSM a domType ()
addIntraEdges i (PathEdge d1 n d2) = do
g <- getICFG
analysis <- gets ifdsAnalysis
-- Predecessors in the ICFG
let intraPredEdges = map toIntraEdge $ lpre g n
intraSuccessors = suc g n
mkIntraEdge ipes successor = map (\d3 -> PathEdge d1 successor d3) ipes
-- Domain elements reachable due to this instruction
let dests = flow analysis d2 i intraPredEdges
inducedEdges = concatMap (mkIntraEdge dests) intraSuccessors
mapM_ propagate inducedEdges
{-# INLINE addIntraEdges #-}
{-# INLINE toIntraEdge #-}
toIntraEdge :: (Node, ICFGEdge) -> CFGEdge
toIntraEdge (_, ie) = e
where
IntraEdge e = ie
addReturnNodeEdges :: (IFDSAnalysis a domType, Ord domType, Show domType)
=> Instruction
-> PathEdge domType
-> IFDSM a domType ()
addReturnNodeEdges i (PathEdge d1 n d2) = do
g <- getICFG
analysis <- gets ifdsAnalysis
let dests = returnNodeFlow analysis d2 i
intraSuccessors = suc g n
inducedEdges = concatMap (mkIntraEdge dests) intraSuccessors
mkIntraEdge ipes successor =
map (\d3 -> PathEdge d1 successor d3) ipes
mapM_ propagate inducedEdges
{-# INLINE addReturnNodeEdges #-}
propagate :: (Ord domType) => PathEdge domType -> IFDSM a domType ()
propagate newEdge = do
s <- get
case newEdge `S.member` pathEdges s of
True -> return ()
False -> put s { pathEdges = newEdge `S.insert` (pathEdges s)
, ifdsWorklist = addWorkItem newEdge (ifdsWorklist s)
}
{-# INLINE addSummaryEdge #-}
addSummaryEdge :: (Ord domType) => SummaryEdge domType -> IFDSM a domType ()
addSummaryEdge se@(SummaryEdge callNode _ d') = do
s <- get
let updatedCache = maybe (S.singleton d') (S.insert d') (M.lookup callNode (summaryValues s))
put s { summaryEdges = S.insert se (summaryEdges s)
, summaryValues = M.insert callNode updatedCache (summaryValues s)
}
{-# INLINE propagate #-}
-- | Build a self loop on the special "null" element for the given
-- entry point
mkInitialEdges :: (IFDSAnalysis a domType) => a -> Module -> Function -> [PathEdge domType]
mkInitialEdges a m f = map makeEdge vs
where
inst0id = instructionUniqueId $ functionEntryInstruction f
vs = entrySetup a m f
makeEdge v = PathEdge v inst0id v
{-
For the main switch statement, there are three cases:
* Call node
Can distinguish these InstNodes because it will always be a call or
invoke instruction
* Exit node
These will always be ret instructions
* Everything else
The current function is always accessible in constant time because any
instruction has constant-time access to its enclosing function.
Matching edges is a simple equality test on the set of all outgoing
edges from ret nodes.
Don't bother building any of the exploded supergraph explicitly. Just
keep a Set of the PathEdges. Reachability queries are then of the form:
S.member (s_p, nodeid) pathEdge
For nodeid in procedure p.
-}