Static Value-Flow Analysis
Loading...
Searching...
No Matches
MTASVFGBuilder.cpp
Go to the documentation of this file.
1//===- MTASVFGBuilder.cpp -- Thread-aware SVFG builder for FSAM ---------===//
2//
3// SVF: Static Value-Flow Analysis
4//
5// Copyright (C) <2013-> <Yulei Sui>
6//
7
8// This program is free software: you can redistribute it and/or modify
9// it under the terms of the GNU Affero General Public License as published by
10// the Free Software Foundation, either version 3 of the License, or
11// (at your option) any later version.
12
13// This program is distributed in the hope that it will be useful,
14// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16// GNU Affero General Public License for more details.
17
18// You should have received a copy of the GNU Affero General Public License
19// along with this program. If not, see <http://www.gnu.org/licenses/>.
20//
21//===----------------------------------------------------------------------===//
22
23/*
24 * MTASVFGBuilder.cpp
25 *
26 * Author: Jiawei Yang
27 *
28 * Implements the FSAM thread-aware value-flow construction (CGO'16, ยง3.3).
29 */
30
31#include "MTA/MTASVFGBuilder.h"
32#include "Graphs/SlicedGraphs.h"
33#include "MSSA/MemSSA.h"
34#include "MSSA/MemPartition.h"
36#include "Util/SVFUtil.h"
37#include "Util/Options.h"
39
40using namespace SVF;
41using namespace SVFUtil;
42
44
45namespace
46{
52template <class BaseMRG>
53class ThreadMRG : public BaseMRG
54{
55public:
56 ThreadMRG(BVDataPTAImpl* p, bool ptrOnly) : BaseMRG(p, ptrOnly) {}
57
58protected:
63 void refineCallsiteModRef(NodeBS& mod, NodeBS& ref,
64 const CallICFGNode* cs, const FunObjVar* callee) override
65 {
66 if (const ThreadCallGraph* tcg = SVFUtil::dyn_cast<ThreadCallGraph>(this->getCallGraph()))
67 if (tcg->hasThreadForkEdge(cs))
68 {
69 ref = this->getRefSideEffectOfFunction(callee);
70 // A fork is a call WITHOUT a return: the spawnee's writes must not
71 // be applied as a mod here (which would kill the spawner's own
72 // value flow past the fork). They reach later reads via the
73 // thread-aware interference edges instead.
74 mod.clear();
75 }
76 }
77
83 void propagateAdditionalModRef(CallGraphNode* callGraphNode,
84 MRGenerator::WorkList& worklist) override
85 {
86 ThreadCallGraph* tcg = SVFUtil::dyn_cast<ThreadCallGraph>(this->getCallGraph());
87 if (tcg == nullptr)
88 return;
90 tcg->getJoinSites(callGraphNode, joinsites);
91 if (joinsites.empty())
92 return;
93 const NodeBS& spawneeMod = this->getModSideEffectOfFunction(callGraphNode->getFunction());
94 for (const CallICFGNode* cs : joinsites)
95 // A join exposes the joined thread's writes at the join point. Those
96 // writes are not necessarily reachable from pthread_join's handle
97 // argument, so use the MOD set as computed for the start routine.
98 if (this->addUnfilteredModSideEffectOfCallSite(cs, spawneeMod))
99 worklist.push(this->getCallGraph()->getCallGraphNode(cs->getCaller())->getId());
100 }
101};
102} // anonymous namespace
103
104// Build a thread-aware MRGenerator wrapping the configured partition strategy, so
105// the MemSSA mod-ref generation carries the FSAM fork/join side effects.
106std::unique_ptr<MRGenerator> MTASVFGBuilder::createMRGenerator(BVDataPTAImpl* pta, bool ptrOnlyMSSA)
107{
108 switch (Options::MemPar())
109 {
111 return std::make_unique<ThreadMRG<DistinctMRG>>(pta, ptrOnlyMSSA);
113 return std::make_unique<ThreadMRG<IntraDisjointMRG>>(pta, ptrOnlyMSSA);
115 return std::make_unique<ThreadMRG<InterDisjointMRG>>(pta, ptrOnlyMSSA);
116 default:
117 assert(false && "unrecognised memory partition strategy");
118 return nullptr;
119 }
120}
121
127{
128 svfg->buildSVFG();
130 connectMHPEdges(svfg->getMSSA()->getPTA());
131}
132
139{
140 NodeBS cpts = formalOut->getPointsTo();
141 const NodeBS& dpts = actualOut->getPointsTo();
142 if (!cpts.intersects(dpts))
143 return;
144 cpts &= dpts;
145
146 SVFGNode* src = svfg->getSVFGNode(formalOut->getId());
147 SVFGNode* dst = svfg->getSVFGNode(actualOut->getId());
148 if (SVFGEdge* edge = svfg->hasInterVFGEdge(src, dst, SVFGEdge::RetIndVF, csId))
149 {
150 SVFUtil::cast<RetIndSVFGEdge>(edge)->addPointsTo(cpts);
151 }
152 else
153 {
154 RetIndSVFGEdge* retEdge = new RetIndSVFGEdge(src, dst, csId);
155 retEdge->addPointsTo(cpts);
156 svfg->addSVFGEdge(retEdge);
157 }
158}
159
167{
168 ThreadCallGraph* tcg =
169 SVFUtil::dyn_cast<ThreadCallGraph>(svfg->getMSSA()->getPTA()->getCallGraph());
170 if (tcg == nullptr)
171 return;
172
173 MemSSA* mssa = svfg->getMSSA();
174 for (SVFG::const_iterator it = svfg->begin(), eit = svfg->end(); it != eit; ++it)
175 {
176 const FormalOUTSVFGNode* formalOut = SVFUtil::dyn_cast<FormalOUTSVFGNode>(it->second);
177 if (formalOut == nullptr)
178 continue;
179
180 ThreadCallGraph::InstSet joinsites;
181 tcg->getJoinSites(tcg->getCallGraphNode(formalOut->getFun()), joinsites);
182 for (const CallICFGNode* cs : joinsites)
183 {
184 if (!mssa->hasCHI(cs))
185 continue;
186 SVFG::ActualOUTSVFGNodeSet& actualOuts = svfg->getActualOUTSVFGNodes(cs);
187 for (NodeID aoId : actualOuts)
188 {
190 SVFUtil::cast<ActualOUTSVFGNode>(svfg->getSVFGNode(aoId));
191 addJoinRetEdge(formalOut, actualOut, svfg->getCallSiteID(cs, formalOut->getFun()));
192 }
193 }
194 }
195}
196
201{
202 for (SVFG::const_iterator it = svfg->begin(), eit = svfg->end(); it != eit; ++it)
203 {
204 const SVFGNode* snode = it->second;
205 const bool isLoad = SVFUtil::isa<LoadSVFGNode>(snode);
206 if (!isLoad && !SVFUtil::isa<StoreSVFGNode>(snode))
207 continue;
208 const StmtSVFGNode* node = SVFUtil::cast<StmtSVFGNode>(snode);
209 const ICFGNode* icfg = node->getICFGNode();
210 if (icfg == nullptr)
211 continue;
212 // Main-solve slice restriction: an interference edge touching a sliced-out
213 // node is inert in the gated FSAM solve, so skip building it here.
214 if (icfgSlice != nullptr && !icfgSlice->isKeptNode(icfg))
215 continue;
216 if (isLoad)
217 ldnodeSet.insert(node);
218 else
219 stnodeSet.insert(node);
220 }
221}
222
227{
228 SVFGNode* srcNode = svfg->getSVFGNode(srcId);
229 SVFGNode* dstNode = svfg->getSVFGNode(dstId);
230
231 if (SVFGEdge* edge = svfg->hasThreadVFGEdge(srcNode, dstNode, SVFGEdge::TheadMHPIndirectVF))
232 {
233 assert(SVFUtil::isa<IndirectSVFGEdge>(edge) && "should be an indirect value-flow edge!");
234 return (SVFUtil::cast<IndirectSVFGEdge>(edge)->addPointsTo(pts.toNodeBS()) ? edge : nullptr);
235 }
236 else
237 {
240 indirectEdge->addPointsTo(pts.toNodeBS());
241 return (svfg->addSVFGEdge(indirectEdge) ? indirectEdge : nullptr);
242 }
243}
244
249{
250 if (prevset.find(n) != prevset.end())
251 return prevset[n];
252
254 Set<const SVFGNode*> worklist;
255 Set<const SVFGNode*> visited;
256
257 for (SVFGEdge::SVFGEdgeSetTy::iterator iter = n->InEdgeBegin(); iter != n->InEdgeEnd(); ++iter)
258 {
259 SVFGEdge* edge = *iter;
260 if (edge->isIndirectVFGEdge())
261 worklist.insert(edge->getSrcNode());
262 }
263
264 while (!worklist.empty())
265 {
266 const SVFGNode* node = *worklist.begin();
267 worklist.erase(worklist.begin());
268 visited.insert(node);
269 if (SVFUtil::isa<StoreSVFGNode>(node))
270 prev.set(node->getId());
271 else
272 {
273 for (SVFGEdge::SVFGEdgeSetTy::iterator iter = node->InEdgeBegin(); iter != node->InEdgeEnd(); ++iter)
274 {
275 SVFGEdge* edge = *iter;
276 if (edge->isIndirectVFGEdge() && visited.find(edge->getSrcNode()) == visited.end())
277 worklist.insert(edge->getSrcNode());
278 }
279 }
280 }
281 prevset[n] = prev;
282 return prev;
283}
284
289{
290 if (succset.find(n) != succset.end())
291 return succset[n];
292
294 Set<const SVFGNode*> worklist;
295 Set<const SVFGNode*> visited;
296
297 for (SVFGEdge::SVFGEdgeSetTy::iterator iter = n->OutEdgeBegin(); iter != n->OutEdgeEnd(); ++iter)
298 {
299 SVFGEdge* edge = *iter;
300 if (edge->isIndirectVFGEdge())
301 worklist.insert(edge->getDstNode());
302 }
303
304 while (!worklist.empty())
305 {
306 const SVFGNode* node = *worklist.begin();
307 worklist.erase(worklist.begin());
308 visited.insert(node);
309 if (SVFUtil::isa<StoreSVFGNode, LoadSVFGNode>(node))
310 succ.set(node->getId());
311 else
312 {
313 for (SVFGEdge::SVFGEdgeSetTy::iterator iter = node->OutEdgeBegin(); iter != node->OutEdgeEnd(); ++iter)
314 {
315 SVFGEdge* edge = *iter;
316 if (edge->isIndirectVFGEdge() && visited.find(edge->getDstNode()) == visited.end())
317 worklist.insert(edge->getDstNode());
318 }
319 }
320 }
321 succset[n] = succ;
322 return succ;
323}
324
329{
330 if (headmap.find(n) != headmap.end())
331 return headmap[n];
332
334 for (NodeID id : prev)
335 {
336 const StmtSVFGNode* prevNode = SVFUtil::dyn_cast<StmtSVFGNode>(svfg->getSVFGNode(id));
337 if (prevNode && lockana->isInSameSpan(prevNode->getICFGNode(), n->getICFGNode()))
338 {
339 headmap[n] = false;
340 return false;
341 }
342 }
343 headmap[n] = true;
344 return true;
345}
346
351{
352 assert(SVFUtil::isa<StoreSVFGNode>(n) && "tail test only for store nodes");
353
354 if (tailmap.find(n) != tailmap.end())
355 return tailmap[n];
356
358 for (NodeID id : succ)
359 {
360 const SVFGNode* sn = svfg->getSVFGNode(id);
361 if (SVFUtil::isa<LoadSVFGNode>(sn))
362 continue;
363 const StmtSVFGNode* succNode = SVFUtil::dyn_cast<StmtSVFGNode>(sn);
364 if (succNode && lockana->isInSameSpan(succNode->getICFGNode(), n->getICFGNode()))
365 {
366 tailmap[n] = false;
367 return false;
368 }
369 }
370 tailmap[n] = true;
371 return true;
372}
373
381{
382 // Per-edge Query set. The endpoints are NOT duplicated into the value --
383 // they are recoverable from the map key -- so the value holds only the
384 // additional in-span witnesses below (empty for the common lock-free case).
385 std::set<const ICFGNode*>& query = threadVFQueryMap[ {s, sp}];
386
387 if (!commonLock)
388 return;
389
390 // Succ_spl(s) = { x in s's span | x is a store, s --o--> x } (tail witnesses).
391 for (NodeID id : getSuccNodes(s))
392 {
393 const SVFGNode* sn = svfg->getSVFGNode(id);
394 if (!SVFUtil::isa<StoreSVFGNode>(sn))
395 continue;
396 const StmtSVFGNode* succNode = SVFUtil::cast<StmtSVFGNode>(sn);
397 if (lockana->isInSameSpan(succNode->getICFGNode(), s->getICFGNode()))
398 query.insert(succNode->getICFGNode());
399 }
400
401 // Pred_spl'(sp) = { x in sp's span | x --o--> sp } (head witnesses).
402 for (NodeID id : getPrevNodes(sp))
403 {
404 const StmtSVFGNode* prevNode = SVFUtil::dyn_cast<StmtSVFGNode>(svfg->getSVFGNode(id));
405 if (prevNode && lockana->isInSameSpan(prevNode->getICFGNode(), sp->getICFGNode()))
406 query.insert(prevNode->getICFGNode());
407 }
408}
409
416{
417 const ICFGNode* i1 = n1->getICFGNode();
418 const ICFGNode* i2 = n2->getICFGNode();
419
421 return;
422
423 // No alias() re-check: the bucketed candidate generator only pairs accesses
424 // whose raw points-to sets share an object, so the intersection below is
425 // non-empty by construction and alias() could never answer NoAlias here.
426 PointsTo pts = pta->getPts(n1->getDstNodeID());
427 pts &= pta->getPts(n2->getSrcNodeID());
428
429 // [THREAD-VF] source extraction runs for every candidate pair (both the
430 // pairs that survive and the ones the lock test prunes), so the sliced ILA
431 // can re-derive whether the edge holds.
433 if (recordThreadVF)
435
436 if (commonLock)
437 {
439 addTDEdge(n1->getId(), n2->getId(), pts);
440 }
441 else
442 {
443 addTDEdge(n1->getId(), n2->getId(), pts);
444 }
445}
446
452{
453 const ICFGNode* i1 = n1->getICFGNode();
454 const ICFGNode* i2 = n2->getICFGNode();
455
457 return;
458
459 // No alias() re-check: see handleStoreLoad -- bucketing already guarantees a
460 // shared raw object.
461 PointsTo pts = pta->getPts(n1->getDstNodeID());
462 pts &= pta->getPts(n2->getDstNodeID());
463
464 // Both directions are candidate thread-aware edges; extract sources for each.
466 if (recordThreadVF)
467 {
470 }
471
472 if (commonLock)
473 {
475 addTDEdge(n1->getId(), n2->getId(), pts);
477 addTDEdge(n2->getId(), n1->getId(), pts);
478 }
479 else
480 {
481 addTDEdge(n1->getId(), n2->getId(), pts);
482 addTDEdge(n2->getId(), n1->getId(), pts);
483 }
484}
485
491{
493
494 // Inverted access index (object -> access-node bitset): unioning the
495 // bitsets per store visits each may-alias pair exactly once, no dedup tables.
498 for (const StmtSVFGNode* store : stnodeSet)
499 for (NodeID obj : pta->getPts(store->getDstNodeID()))
500 objToStoreIds[obj].set(store->getId());
501 for (const StmtSVFGNode* load : ldnodeSet)
502 for (NodeID obj : pta->getPts(load->getSrcNodeID()))
503 objToLoadIds[obj].set(load->getId());
504
505 for (const StmtSVFGNode* store : stnodeSet)
506 {
508 for (NodeID obj : pta->getPts(store->getDstNodeID()))
509 {
512 if (lIt != objToLoadIds.end())
513 candLoads |= lIt->second;
514 }
515
516 for (NodeID loadId : candLoads)
517 handleStoreLoad(store, SVFUtil::cast<StmtSVFGNode>(svfg->getSVFGNode(loadId)), pta);
518
519 // Visit each unordered store pair once: only partners with a larger id.
520 const NodeID storeId = store->getId();
522 {
523 if (otherId <= storeId)
524 continue;
525 handleStoreStore(store, SVFUtil::cast<StmtSVFGNode>(svfg->getSVFGNode(otherId)), pta);
526 }
527 }
528}
unsigned u32_t
Definition CommandLine.h:18
cJSON * p
Definition cJSON.cpp:2559
if(prebuffer< 0)
Definition cJSON.cpp:1269
newitem prev
Definition cJSON.cpp:2285
cJSON * n
Definition cJSON.cpp:2558
const FunObjVar * getFunction() const
Get function of this call node.
Definition CallGraph.h:191
const CallGraphNode * getCallGraphNode(const std::string &name) const
Get call graph node.
IDToNodeMapTy::const_iterator const_iterator
iterator OutEdgeEnd()
iterator OutEdgeBegin()
iterators
iterator InEdgeBegin()
iterator InEdgeEnd()
virtual const FunObjVar * getFun() const
Return the function of this ICFGNode.
Definition ICFGNode.h:75
bool isProtectedByCommonLock(const ICFGNode *i1, const ICFGNode *i2)
bool isInSameSpan(const ICFGNode *I1, const ICFGNode *I2)
virtual bool mayHappenInParallel(const ICFGNode *i1, const ICFGNode *i2)
Interface to query whether two instructions may happen-in-parallel.
Definition MHP.cpp:763
LockAnalysis * lockana
static u32_t numOfNewSVFGEdges
Number of thread-aware (interference) SVFG edges added.
void connectMHPEdges(PointerAnalysis *pta)
Connect inter-thread (interference) value-flow edges for MHP pairs.
std::unique_ptr< MRGenerator > createMRGenerator(BVDataPTAImpl *pta, bool ptrOnlyMSSA) override
void handleStoreLoad(const StmtSVFGNode *n1, const StmtSVFGNode *n2, PointerAnalysis *pta)
Map< const StmtSVFGNode *, SVFGNodeIDSet > prevset
void handleStoreStore(const StmtSVFGNode *n1, const StmtSVFGNode *n2, PointerAnalysis *pta)
SVFGEdge * addTDEdge(NodeID srcId, NodeID dstId, const PointsTo &pts)
Add a thread-MHP indirect value-flow edge srcId -> dstId carrying pts.
bool isHeadOfSpan(const StmtSVFGNode *n)
void recordThreadVFSource(const StmtSVFGNode *s, const StmtSVFGNode *sp, bool commonLock)
bool recordThreadVF
false = skip [THREAD-VF] recording
SVFGNodeSet ldnodeSet
all load SVFG nodes
SVFGNodeIDSet getSuccNodes(const StmtSVFGNode *n)
SVFGNodeSet stnodeSet
all store SVFG nodes
SVFGNodeIDSet getPrevNodes(const StmtSVFGNode *n)
Lock-span head/tail tests (non-interference lock-pair pruning).
bool isTailOfSpan(const StmtSVFGNode *n)
const SlicedICFGView * icfgSlice
Main-solve configuration (see configureForMainSolve); defaults suit VFG_pre.
Map< const StmtSVFGNode *, SVFGNodeIDSet > succset
Map< const StmtSVFGNode *, bool > headmap
Map< const StmtSVFGNode *, bool > tailmap
std::map< ThreadVFEdge, std::set< const ICFGNode * > > threadVFQueryMap
[THREAD-VF] per-edge query map (see getThreadVFQueryMap).
void addJoinRetEdge(const FormalOUTSVFGNode *formalOut, const ActualOUTSVFGNode *actualOut, CallSiteID csId)
void buildSVFG() override
Rewrite the SVFG build hook: build the stock SVFG, then add MHP edges.
bool hasCHI(const PAGEdge *inst) const
Definition MemSSA.h:338
@ InterDisjoint
Definition MemSSA.h:117
@ IntraDisjoint
Definition MemSSA.h:116
static const OptionMap< u32_t > MemPar
Definition Options.h:141
virtual const PointsTo & getPts(NodeID ptr)=0
Get points-to targets of a pointer. It needs to be implemented in child class.
std::unique_ptr< SVFG > svfg
NodeID getId() const
Get ID.
Definition SVFValue.h:158
bool isKeptNode(const ICFGNode *node) const
Check if a node is in the sliced view.
bool intersects(const SparseBitVector< ElementSize > *RHS) const
OrderedSet< const CallICFGNode *, CallSiteIdCmp > InstSet
void getJoinSites(const CallGraphNode *routine, InstSet &csSet)
@ TheadMHPIndirectVF
Definition VFGEdge.h:59
virtual const ICFGNode * getICFGNode() const
Return corresponding ICFG node.
Definition VFGNode.h:67
for isBitcode
Definition BasicTypes.h:70
unsigned CallSiteID
Definition GeneralType.h:78
u32_t NodeID
Definition GeneralType.h:76
std::unordered_map< Key, Value, Hash, KeyEqual, Allocator > Map
Definition GeneralType.h:56
llvm::IRBuilder IRBuilder
Definition BasicTypes.h:76