Static Value-Flow Analysis
Loading...
Searching...
No Matches
VersionedFlowSensitive.cpp
Go to the documentation of this file.
1//===- VersionedFlowSensitive.cpp -- Versioned flow-sensitive pointer analysis------------//
2
3/*
4 * VersionedFlowSensitive.cpp
5 *
6 * Created on: Jun 26, 2020
7 * Author: Mohamad Barbar
8 */
9
10#include "WPA/Andersen.h"
12#include "Util/Options.h"
14#include <iostream>
15#include <queue>
16#include <thread>
17#include <mutex>
18
19using namespace SVF;
20
23
25{
26 assert(version != invalidVersion && "VersionedFlowSensitive::atKey: trying to use an invalid version!");
27 return std::make_pair(var, version);
28}
29
31 : FlowSensitive(_pag, type)
32{
35 // We'll grab vPtD in initialize.
36
37 for (SVFIR::const_iterator it = pag->begin(); it != pag->end(); ++it)
38 {
39 if (SVFUtil::isa<ObjVar>(it->second)) equivalentObject[it->first] = it->first;
40 }
41
42 assert(!Options::OPTSVFG() && "VFS: -opt-svfg not currently supported with VFS.");
43}
44
46{
48 // Overwrite the stat FlowSensitive::initialize gave us.
49 delete stat;
51
53
56 consume.resize(svfg->getTotalNodeNum());
57 yield.resize(svfg->getTotalNodeNum());
58
59 prelabel();
60 meldLabel();
61
63}
64
66{
68 // vPtD->dumpPTData();
69 // dumpReliances();
70 // dumpLocVersionMaps();
71}
72
74{
75 double start = stat->getClk(true);
76 for (SVFG::iterator it = svfg->begin(); it != svfg->end(); ++it)
77 {
78 NodeID l = it->first;
79 const SVFGNode *ln = it->second;
80
81 if (const StoreSVFGNode *stn = SVFUtil::dyn_cast<StoreSVFGNode>(ln))
82 {
83 // l: *p = q.
84 // If p points to o (Andersen's), l yields a new version for o.
85 NodeID p = stn->getPAGDstNodeID();
86 for (NodeID o : ander->getPts(p))
87 {
88 prelabeledObjects.insert(o);
89 }
90
92
93 if (ander->getPts(p).count() != 0) ++numPrelabeledNodes;
94 }
95 else if (delta(l))
96 {
97 // The outgoing edges are not only what will later be propagated. SVFGOPT may
98 // move around nodes such that there can be an MRSVFGNode with no incoming or
99 // outgoing edges which will be added at runtime. In essence, we can no
100 // longer rely on the outgoing edges of a delta node when SVFGOPT is enabled.
101 const MRSVFGNode *mr = SVFUtil::dyn_cast<MRSVFGNode>(ln);
102 if (mr != nullptr)
103 {
104 for (const NodeID o : mr->getPointsTo())
105 {
106 prelabeledObjects.insert(o);
107 }
108
109 // Push into worklist because its consume == its yield.
111 if (mr->getPointsTo().count() != 0) ++numPrelabeledNodes;
112 }
113 }
114 }
115
116 double end = stat->getClk(true);
118}
119
121{
122 double start = stat->getClk(true);
123
124 assert(Options::VersioningThreads() > 0 && "VFS::meldLabel: number of versioning threads must be > 0!");
125
126 // Nodes which have at least one object on them given a prelabel + the Andersen's points-to
127 // set of interest so we don't keep calling getPts. For Store nodes, we'll fill that in, for
128 // MR nodes, we won't as its getPointsTo is cheap.
129 // TODO: preferably we cache both for ease and to avoid the dyn_cast/isa, but Andersen's points-to
130 // sets are PointsTo and MR's sets are NodeBS, which are incompatible types. Maybe when we can
131 // use std::option.
132 std::vector<std::pair<const SVFGNode *, const PointsTo *>> prelabeledNodes;
133 // Fast query for the above.
134 std::vector<bool> isPrelabeled(svfg->getTotalNodeNum(), false);
135 while (!vWorklist.empty())
136 {
137 const NodeID n = vWorklist.pop();
138 isPrelabeled[n] = true;
139
140 const SVFGNode *sn = svfg->getSVFGNode(n);
141 const PointsTo *nPts = nullptr;
142 if (const StoreSVFGNode *store = SVFUtil::dyn_cast<StoreSVFGNode>(sn))
143 {
144 const NodeID p = store->getPAGDstNodeID();
145 nPts = &(this->ander->getPts(p));
146 }
147
148 prelabeledNodes.push_back(std::make_pair(sn, nPts));
149 }
150
151 // Delta, delta source, store, and load nodes, which require versions during
152 // solving, unlike other nodes with which we can make do with the reliance map.
153 std::vector<NodeID> nodesWhichNeedVersions;
154 for (SVFG::const_iterator it = svfg->begin(); it != svfg->end(); ++it)
155 {
156 const NodeID n = it->first;
157 if (delta(n) || deltaSource(n) || isStore(n) || isLoad(n)) nodesWhichNeedVersions.push_back(n);
158 }
159
160 std::mutex *versionMutexes = new std::mutex[nodesWhichNeedVersions.size()];
161
162 // Map of footprints to the canonical object "owning" the footprint.
164
165 std::queue<NodeID> objectQueue;
166 for (const NodeID o : prelabeledObjects)
167 {
168 // "Touch" maps with o so we don't need to lock on them.
171 objectQueue.push(o);
172 }
173
174 std::mutex objectQueueMutex;
175 std::mutex footprintOwnerMutex;
176
180 (const unsigned thread)
181 {
182 while (true)
183 {
184 NodeID o;
185 {
186 std::lock_guard<std::mutex> guard(objectQueueMutex);
187 // No more objects? Done.
188 if (objectQueue.empty()) return;
189 o = objectQueue.front();
190 objectQueue.pop();
191 }
192
193 // 1. Compute the SCCs for the nodes on the graph overlay of o.
194 // For starting nodes, we only need those which did prelabeling for o specifically.
195 // TODO: maybe we should move this to prelabel with a map (o -> starting nodes).
196 std::vector<const SVFGNode *> osStartingNodes;
197 for (std::pair<const SVFGNode *, const PointsTo *> snPts : prelabeledNodes)
198 {
199 const SVFGNode *sn = snPts.first;
200 const PointsTo *pts = snPts.second;
201 if (pts != nullptr)
202 {
203 if (pts->test(o)) osStartingNodes.push_back(sn);
204 }
205 else if (const MRSVFGNode *mr = SVFUtil::dyn_cast<MRSVFGNode>(sn))
206 {
207 if (mr->getPointsTo().test(o)) osStartingNodes.push_back(sn);
208 }
209 else
210 {
211 assert(false && "VFS::meldLabel: unexpected prelabeled node!");
212 }
213 }
214
215 std::vector<int> partOf;
216 std::vector<const IndirectSVFGEdge *> footprint;
217 unsigned numSCCs = SCC::detectSCCs(this, this->svfg, o, osStartingNodes, partOf, footprint);
218
219 // 2. Skip any further processing of a footprint we have seen before.
220 {
221 std::lock_guard<std::mutex> guard(footprintOwnerMutex);
224 if (canonOwner == footprintOwner.end())
225 {
226 this->equivalentObject[o] = o;
227 footprintOwner[footprint] = o;
228 }
229 else
230 {
231 this->equivalentObject[o] = canonOwner->second;
232 // Same version and stmt reliance as the canonical. During solving we cannot just reuse
233 // the canonical object's reliance because it may change due to on-the-fly call graph
234 // construction. Something like copy-on-write could be good... probably negligible.
235 this->versionReliance.at(o) = this->versionReliance.at(canonOwner->second);
236 this->stmtReliance.at(o) = this->stmtReliance.at(canonOwner->second);
237 continue;
238 }
239 }
240
241 // 3. a. Initialise the MeldVersion of prelabeled nodes (SCCs).
242 // b. Initialise a todo list of all the nodes we need to version,
243 // sorted according to topological order.
244 // We will use a map of sccs to meld versions for what is consumed.
245 std::vector<MeldVersion> sccToMeldVersion(numSCCs);
246 // At stores, what is consumed is different to what is yielded, so we
247 // maintain that separately.
249 // SVFG nodes of interest -- those part of an SCC from the starting nodes.
250 std::vector<NodeID> todoList;
251 unsigned bit = 0;
252 // To calculate reachable nodes, we can see what nodes n exist where
253 // partOf[n] != -1. Since the SVFG can be large this can be expensive.
254 // Instead, we can gather this from the edges in the footprint and
255 // the starting nodes (incase such nodes have no edges).
256 // TODO: should be able to do this better: too many redundant inserts.
258 for (const SVFGNode *sn : osStartingNodes) reachableNodes.insert(sn->getId());
259 for (const SVFGEdge *se : footprint)
260 {
261 reachableNodes.insert(se->getSrcNode()->getId());
262 reachableNodes.insert(se->getDstNode()->getId());
263 }
264
265 for (const NodeID n : reachableNodes)
266 {
267 if (isPrelabeled[n])
268 {
269 if (this->isStore(n)) storesYieldedMeldVersion[n].set(bit);
270 else sccToMeldVersion[partOf[n]].set(bit);
271 ++bit;
272 }
273
274 todoList.push_back(n);
275 }
276
277 // Sort topologically so each nodes is only visited once.
278 auto cmp = [&partOf](const NodeID a, const NodeID b)
279 {
280 return partOf[a] > partOf[b];
281 };
282 std::sort(todoList.begin(), todoList.end(), cmp);
283
284 // 4. a. Do meld versioning.
285 // b. Determine SCC reliances.
286 // c. Build a footprint for o (all edges which it is found on).
287 // d. Determine which SCCs belong to stores.
288
289 // sccReliance[x] = { y_1, y_2, ... } if there exists an edge from a node
290 // in SCC x to SCC y_i.
291 std::vector<Set<int>> sccReliance(numSCCs);
292 // Maps SCC to the store it corresponds to or -1 if it doesn't. TODO: unsigned vs signed -- nasty.
293 std::vector<int> storeSCC(numSCCs, -1);
294 for (size_t i = 0; i < todoList.size(); ++i)
295 {
296 const NodeID n = todoList[i];
297 const SVFGNode *sn = this->svfg->getSVFGNode(n);
298 const bool nIsStore = this->isStore(n);
299
300 int nSCC = partOf[n];
301 if (nIsStore) storeSCC[nSCC] = n;
302
303 // Given n -> m, the yielded version of n will be melded into m.
304 // For stores, that is in storesYieldedMeldVersion, otherwise, consume == yield and
305 // we can just use sccToMeldVersion.
307 for (const SVFGEdge *e : sn->getOutEdges())
308 {
309 const IndirectSVFGEdge *ie = SVFUtil::dyn_cast<IndirectSVFGEdge>(e);
310 if (!ie) continue;
311
312 const NodeID m = ie->getDstNode()->getId();
313 // Ignoreedges which don't involve o.
314 if (!ie->getPointsTo().test(o)) continue;
315
316 int mSCC = partOf[m];
317
318 // There is an edge from the SCC n belongs to that m belongs to.
319 sccReliance[nSCC].insert(mSCC);
320
321 // Ignore edges to delta nodes (prelabeled consume).
322 // No point propagating when n's SCC == m's SCC (same meld version there)
323 // except when it is a store, because we are actually propagating n's yielded
324 // into m's consumed. Store nodes are in their own SCCs, so it is a self
325 // loop on a store node.
326 if (!this->delta(m) && (nSCC != mSCC || nIsStore))
327 {
329 }
330 }
331 }
332
333 // 5. Transform meld versions belonging to SCCs into versions.
335 std::vector<Version> sccToVersion(numSCCs, invalidVersion);
337 for (u32_t scc = 0; scc < sccToMeldVersion.size(); ++scc)
338 {
341 Version v = foundVersion == mvv.end() ? mvv[mv] = ++curVersion : foundVersion->second;
342 sccToVersion[scc] = v;
343 }
344
345 sccToMeldVersion.clear();
346
347 // Same for storesYieldedMeldVersion.
349 for (auto const& nmv : storesYieldedMeldVersion)
350 {
351 const NodeID n = nmv.first;
352 const MeldVersion &mv = nmv.second;
353
355 Version v = foundVersion == mvv.end() ? mvv[mv] = ++curVersion : foundVersion->second;
357 }
358
360
361 mvv.clear();
362
363 // 6. From SCC reliance, determine version reliances.
365 for (u32_t scc = 0; scc < numSCCs; ++scc)
366 {
367 if (sccReliance[scc].empty()) continue;
368
369 // Some consume relies on a yield. When it's a store, we need to pick whether to
370 // use the consume or yield unlike when it is not because they are the same.
371 const Version version
373
374 std::vector<Version> &reliantVersions = osVersionReliance[version];
375 for (const int reliantSCC : sccReliance[scc])
376 {
378 if (version != reliantVersion)
379 {
380 // sccReliance is a set, no need to worry about duplicates.
382 }
383 }
384 }
385
386 // 7. a. Save versions for nodes which need them.
387 // b. Fill in stmtReliance.
388 // TODO: maybe randomize iteration order for less contention? Needs profiling.
390 for (size_t i = 0; i < nodesWhichNeedVersions.size(); ++i)
391 {
393 std::mutex &mutex = versionMutexes[i];
394
395 const int scc = partOf[n];
396 if (scc == -1) continue;
397
398 std::lock_guard<std::mutex> guard(mutex);
399
400 const Version c = sccToVersion[scc];
401 if (c != invalidVersion)
402 {
403 this->setConsume(n, o, c);
404 if (this->isStore(n) || this->isLoad(n)) osStmtReliance[c].set(n);
405 }
406
407 if (this->isStore(n))
408 {
410 if (yIt != storesYieldedVersion.end()) this->setYield(n, o, yIt->second);
411 }
412 }
413 }
414 };
415
416 std::vector<std::thread> workers;
417 for (unsigned i = 0; i < Options::VersioningThreads(); ++i) workers.push_back(std::thread(meldVersionWorker, i));
418 for (std::thread &worker : workers) worker.join();
419
420 delete[] versionMutexes;
421
422 double end = stat->getClk(true);
424}
425
427{
428 // Meld operator is union of bit vectors.
429 return mv1 |= mv2;
430}
431
433{
434 assert(l < deltaMap.size() && "VFS::delta: deltaMap is missing SVFG nodes!");
435 return deltaMap[l];
436}
437
439{
440 assert(l < deltaSourceMap.size() && "VFS::delta: deltaSourceMap is missing SVFG nodes!");
441 return deltaSourceMap[l];
442}
443
445{
446 isStoreMap.resize(svfg->getTotalNodeNum(), false);
447 isLoadMap.resize(svfg->getTotalNodeNum(), false);
448 for (SVFG::const_iterator it = svfg->begin(); it != svfg->end(); ++it)
449 {
450 if (SVFUtil::isa<StoreSVFGNode>(it->second)) isStoreMap[it->first] = true;
451 else if (SVFUtil::isa<LoadSVFGNode>(it->second)) isLoadMap[it->first] = true;
452 }
453}
454
456{
457 assert(l < isStoreMap.size() && "VFS::isStore: isStoreMap is missing SVFG nodes!");
458 return isStoreMap[l];
459}
460
462{
463 assert(l < isLoadMap.size() && "VFS::isLoad: isLoadMap is missing SVFG nodes!");
464 return isLoadMap[l];
465}
466
468{
469 deltaMap.resize(svfg->getTotalNodeNum(), false);
470
471 // Call block nodes corresponding to all delta nodes.
473
474 for (SVFG::const_iterator it = svfg->begin(); it != svfg->end(); ++it)
475 {
476 const NodeID l = it->first;
477 const SVFGNode *s = it->second;
478
479 // Cases:
480 // * Function entry: can get new incoming indirect edges through ind. callsites.
481 // * Callsite returns: can get new incoming indirect edges if the callsite is indirect.
482 // * Otherwise: static.
483 bool isDelta = false;
484 if (const SVFFunction *fn = svfg->isFunEntrySVFGNode(s))
485 {
489 isDelta = !callsites.empty();
490
491 if (isDelta)
492 {
493 // TODO: could we use deltaCBNs in the call above, avoiding this loop?
494 for (const CallICFGNode *cbn : callsites) deltaCBNs.insert(cbn);
495 }
496 }
497 else if (const CallICFGNode *cbn = svfg->isCallSiteRetSVFGNode(s))
498 {
499 isDelta = cbn->isIndirectCall();
500 if (isDelta) deltaCBNs.insert(cbn);
501 }
502
503 deltaMap[l] = isDelta;
504 }
505
506 deltaSourceMap.resize(svfg->getTotalNodeNum(), false);
507
508 for (SVFG::const_iterator it = svfg->begin(); it != svfg->end(); ++it)
509 {
510 const NodeID l = it->first;
511 const SVFGNode *s = it->second;
512
513 if (const CallICFGNode *cbn = SVFUtil::dyn_cast<CallICFGNode>(s->getICFGNode()))
514 {
515 if (deltaCBNs.find(cbn) != deltaCBNs.end()) deltaSourceMap[l] = true;
516 }
517
518 // TODO: this is an over-approximation but it sound, marking every formal out as
519 // a delta-source.
520 if (SVFUtil::isa<FormalOUTSVFGNode>(s)) deltaSourceMap[l] = true;
521 }
522}
523
525{
526 for (SVFG::iterator nodeIt = svfg->begin(); nodeIt != svfg->end(); ++nodeIt)
527 {
528 SVFGNode *sn = nodeIt->second;
529
531 std::vector<SVFGEdge *> toDeleteFromIn;
532 for (SVFGEdge *e : inEdges)
533 {
534 if (SVFUtil::isa<IndirectSVFGEdge>(e)) toDeleteFromIn.push_back(e);
535 }
536
538
539 // Only need to iterate over incoming edges for each node because edges
540 // will be deleted from in/out through removeSVFGEdge.
541 }
542
543 setGraph(svfg);
544}
545
547{
548 double start = stat->getClk();
549
550 const std::vector<Version> &reliantVersions = getReliantVersions(o, v);
552 {
553 propagateVersion(o, v, r, false);
554 }
555
556 double end = stat->getClk();
558}
559
560void VersionedFlowSensitive::propagateVersion(const NodeID o, const Version v, const Version vp, bool time/*=true*/)
561{
562 double start = time ? stat->getClk() : 0.0;
563
564 const VersionedVar srcVar = atKey(o, v);
565 const VersionedVar dstVar = atKey(o, vp);
566 if (vPtD->unionPts(dstVar, srcVar))
567 {
568 // o:vp has changed.
569 // Add the dummy propagation node to tell the solver to propagate it later.
570 const DummyVersionPropSVFGNode *dvp = nullptr;
571 VarToPropNodeMap::const_iterator dvpIt = versionedVarToPropNode.find(dstVar);
572 if (dvpIt == versionedVarToPropNode.end())
573 {
576 }
577 else dvp = dvpIt->second;
578
579 assert(dvp != nullptr && "VFS::propagateVersion: propagation dummy node not found?");
580 pushIntoWorklist(dvp->getId());
581
582 // Notify nodes which rely on o:vp that it changed.
584 }
585
586 double end = time ? stat->getClk() : 0.0;
587 if (time) versionPropTime += (end - start) / TIMEINTERVAL;
588}
589
591{
593 // Handle DummyVersPropSVFGNode here so we don't have to override the long
594 // processSVFGNode. We also don't call propagate based on its result.
595 if (const DummyVersionPropSVFGNode *dvp = SVFUtil::dyn_cast<DummyVersionPropSVFGNode>(sn))
596 {
597 propagateVersion(dvp->getObject(), dvp->getVersion());
598 }
599 else if (processSVFGNode(sn))
600 {
601 propagate(&sn);
602 }
603}
604
606{
607 for (const SVFGEdge *e : newEdges)
608 {
609 SVFGNode *dstNode = e->getDstNode();
610 NodeID src = e->getSrcNode()->getId();
611 NodeID dst = dstNode->getId();
612
613 if (SVFUtil::isa<PHISVFGNode>(dstNode)
614 || SVFUtil::isa<FormalParmSVFGNode>(dstNode)
615 || SVFUtil::isa<ActualRetSVFGNode>(dstNode))
616 {
617 pushIntoWorklist(dst);
618 }
619 else
620 {
621 const IndirectSVFGEdge *ie = SVFUtil::dyn_cast<IndirectSVFGEdge>(e);
622 assert(ie != nullptr && "VFS::updateConnectedNodes: given direct edge?");
623
624 assert(delta(dst) && "VFS::updateConnectedNodes: new edges should be to delta nodes!");
625 assert(deltaSource(src) && "VFS::updateConnectedNodes: new indirect edges should be from delta source nodes!");
626
627 const NodeBS &ept = ie->getPointsTo();
628 // For every o, such that src --o--> dst, we need to set up reliance (and propagate).
629 for (const NodeID o : ept)
630 {
631 Version srcY = getYield(src, o);
632 if (srcY == invalidVersion) continue;
633 Version dstC = getConsume(dst, o);
634 if (dstC == invalidVersion) continue;
635
636 std::vector<Version> &versionsRelyingOnSrcY = getReliantVersions(o, srcY);
637 if (std::find(versionsRelyingOnSrcY.begin(), versionsRelyingOnSrcY.end(), dstC) == versionsRelyingOnSrcY.end())
638 {
639 versionsRelyingOnSrcY.push_back(dstC);
641 }
642 }
643 }
644 }
645}
646
648{
649 double start = stat->getClk();
650
651 bool changed = false;
652
653 // l: p = *q
654 NodeID l = load->getId();
655 NodeID p = load->getPAGDstNodeID();
656 NodeID q = load->getPAGSrcNodeID();
657
658 const PointsTo& qpt = getPts(q);
659 // p = *q, the type of p must be a pointer
660 if (load->getPAGDstNode()->isPointer())
661 {
662 for (NodeID o : qpt)
663 {
664 if (pag->isConstantObj(o)) continue;
665
666 const Version c = getConsume(l, o);
667 if (c != invalidVersion && vPtD->unionPts(p, atKey(o, c)))
668 {
669 changed = true;
670 }
671
673 {
677 for (NodeID of : fields)
678 {
679 const Version c = getConsume(l, of);
680 if (c != invalidVersion && vPtD->unionPts(p, atKey(of, c)))
681 {
682 changed = true;
683 }
684 }
685 }
686 }
687 }
688 double end = stat->getClk();
689 loadTime += (end - start) / TIMEINTERVAL;
690 return changed;
691}
692
694{
695 NodeID p = store->getPAGDstNodeID();
696 const PointsTo &ppt = getPts(p);
697
698 if (ppt.empty()) return false;
699
700 NodeID q = store->getPAGSrcNodeID();
701 const PointsTo &qpt = getPts(q);
702
703 NodeID l = store->getId();
704 // l: *p = q
705
706 double start = stat->getClk();
707 bool changed = false;
708 // The version for these objects would be y_l(o).
710
711 if (!qpt.empty())
712 {
713 // *p = q, the type of q must be a pointer
714 if (store->getPAGSrcNode()->isPointer())
715 {
716 for (NodeID o : ppt)
717 {
718 if (pag->isConstantObj(o)) continue;
719
720 const Version y = getYield(l, o);
721 if (y != invalidVersion && vPtD->unionPts(atKey(o, y), q))
722 {
723 changed = true;
724 changedObjects.set(o);
725 }
726 }
727 }
728 }
729
730 double end = stat->getClk();
731 storeTime += (end - start) / TIMEINTERVAL;
732
733 double updateStart = stat->getClk();
734
735 NodeID singleton = 0;
736 bool isSU = isStrongUpdate(store, singleton);
737 if (isSU) svfgHasSU.set(l);
738 else svfgHasSU.reset(l);
739
740 // For all objects, perform pts(o:y) = pts(o:y) U pts(o:c) at loc,
741 // except when a strong update is taking place.
742 for (const ObjToVersionMap::value_type &oc : consume[l])
743 {
744 const NodeID o = oc.first;
745 const Version c = oc.second;
746
747 // Strong-updated; don't propagate.
748 if (isSU && o == singleton) continue;
749
750 const Version y = getYield(l, o);
751 if (y != invalidVersion && vPtD->unionPts(atKey(o, y), atKey(o, c)))
752 {
753 changed = true;
754 changedObjects.set(o);
755 }
756 }
757
758 double updateEnd = stat->getClk();
760
761 // Changed objects need to be propagated. Time here should be inconsequential
762 // *except* for time taken for propagateVersion, which will time itself.
763 if (!changedObjects.empty())
764 {
765 for (const NodeID o : changedObjects)
766 {
767 // Definitely has a yielded version (came from prelabelling) as these are
768 // the changed objects which must've been pointed to in Andersen's too.
769 const Version y = getYield(l, o);
771
772 // Some o/v pairs changed: statements need to know.
774 }
775 }
776
777 return changed;
778}
779
781{
782 std::vector<std::pair<unsigned, unsigned>> keys;
783 for (SVFIR::iterator pit = pag->begin(); pit != pag->end(); ++pit)
784 {
785 unsigned occ = 1;
786 unsigned v = pit->first;
787 if (Options::PredictPtOcc() && pag->getObject(v) != nullptr) occ = stmtReliance[v].size() + 1;
788 assert(occ != 0);
789 keys.push_back(std::make_pair(v, occ));
790 }
791
792 PointsTo::MappingPtr nodeMapping =
793 std::make_shared<std::vector<NodeID>>(NodeIDAllocator::Clusterer::cluster(ander, keys, candidateMappings, "aux-ander"));
794 PointsTo::MappingPtr reverseNodeMapping =
795 std::make_shared<std::vector<NodeID>>(NodeIDAllocator::Clusterer::getReverseNodeMapping(*nodeMapping));
796
797 PointsTo::setCurrentBestNodeMapping(nodeMapping, reverseNodeMapping);
798}
799
801{
803 const NodeID op = canonObjectIt == equivalentObject.end() ? o : canonObjectIt->second;
804
805 const ObjToVersionMap &ovm = lvm[l];
806 const ObjToVersionMap::const_iterator foundVersion = ovm.find(op);
807 return foundVersion == ovm.end() ? invalidVersion : foundVersion->second;
808}
809
811{
812 return getVersion(l, o, consume);
813}
814
816{
817 // Non-store: consume == yield.
818 if (isStore(l)) return getVersion(l, o, yield);
819 else return getVersion(l, o, consume);
820}
821
823{
825 ovm[o] = v;
826}
827
829{
830 setVersion(l, o, v, consume);
831}
832
834{
835 // Non-store: consume == yield.
836 if (isStore(l)) setVersion(l, o, v, yield);
837 else setVersion(l, o, v, consume);
838}
839
841{
842 return versionReliance[o][v];
843}
844
849
851{
852 SVFUtil::outs() << "# Version reliances\n";
853 for (const Map<NodeID, Map<Version, std::vector<Version>>>::value_type &ovrv : versionReliance)
854 {
855 NodeID o = ovrv.first;
856 SVFUtil::outs() << " Object " << o << "\n";
857 for (const Map<Version, std::vector<Version>>::value_type& vrv : ovrv.second)
858 {
859 Version v = vrv.first;
860 SVFUtil::outs() << " Version " << v << " is a reliance for: ";
861
862 bool first = true;
863 for (Version rv : vrv.second)
864 {
865 if (!first)
866 {
867 SVFUtil::outs() << ", ";
868 }
869
870 SVFUtil::outs() << rv;
871 first = false;
872 }
873
874 SVFUtil::outs() << "\n";
875 }
876 }
877
878 SVFUtil::outs() << "# Statement reliances\n";
879 for (const Map<NodeID, Map<Version, NodeBS>>::value_type &ovss : stmtReliance)
880 {
881 NodeID o = ovss.first;
882 SVFUtil::outs() << " Object " << o << "\n";
883
884 for (const Map<Version, NodeBS>::value_type &vss : ovss.second)
885 {
886 Version v = vss.first;
887 SVFUtil::outs() << " Version " << v << " is a reliance for statements: ";
888
889 const NodeBS &ss = vss.second;
890 bool first = true;
891 for (NodeID s : ss)
892 {
893 if (!first)
894 {
895 SVFUtil::outs() << ", ";
896 }
897
898 SVFUtil::outs() << s;
899 first = false;
900 }
901
902 SVFUtil::outs() << "\n";
903 }
904 }
905}
906
908{
909 SVFUtil::outs() << "# LocVersion Maps\n";
910 for (SVFG::iterator it = svfg->begin(); it != svfg->end(); ++it)
911 {
912 const NodeID loc = it->first;
913 bool locPrinted = false;
914 for (const LocVersionMap *lvm :
915 {
916 &consume, &yield
917 })
918 {
919 if (lvm->at(loc).empty()) continue;
920 if (!locPrinted)
921 {
922 SVFUtil::outs() << " " << "SVFG node " << loc << "\n";
923 locPrinted = true;
924 }
925
926 SVFUtil::outs() << " " << (lvm == &consume ? "Consume " : "Yield ") << ": ";
927
928 bool first = true;
929 for (const ObjToVersionMap::value_type &ov : lvm->at(loc))
930 {
931 const NodeID o = ov.first;
932 const Version v = ov.second;
933 SVFUtil::outs() << (first ? "" : ", ") << "<" << o << ", " << v << ">";
934 first = false;
935 }
936
937 SVFUtil::outs() << "\n";
938 }
939 }
940
941}
942
944{
945 SVFUtil::outs() << "[ ";
946 bool first = true;
947 for (unsigned e : v)
948 {
949 if (!first)
950 {
951 SVFUtil::outs() << ", ";
952 }
953
954 SVFUtil::outs() << e;
955 first = false;
956 }
957
958 SVFUtil::outs() << " ]";
959}
960
962{
964 initialize();
966 if(!filename.empty())
967 {
968 SVFUtil::outs() << "Loading versioned pointer analysis results from '" << filename << "'...";
969
970 std::ifstream F(filename.c_str());
971 if (!F.is_open())
972 {
973 SVFUtil::outs() << " error opening file for reading!\n";
974 return ;
975 }
977
979
981
983
985
986 // Update callgraph
988
989 F.close();
990 SVFUtil::outs() << "\n";
991 }
992
994 finalize();
995}
996
998{
1000 initialize();
1001 if(!filename.empty())
1004 if(!filename.empty())
1005 {
1008 }
1010 finalize();
1011}
1012
1014{
1015 SVFUtil::outs() << "Storing Versioned Analysis Result to '" << filename << "'...";
1016 std::error_code err;
1017 std::fstream f(filename.c_str(), std::ios_base::app);
1018 if (!f.good())
1019 {
1020 SVFUtil::outs() << " error opening file for writing!\n";
1021 return;
1022 }
1023
1025 {
1026 &this->consume, &this->yield
1027 })
1028 {
1030 {
1031 for (const VersionedFlowSensitive::ObjToVersionMap::value_type &ov : lov)
1032 {
1033 const NodeID o = ov.first;
1034 const Version v = ov.second;
1035 if (vPtD->getPts(atKey(o, v)).empty()) continue;
1036
1037 f <<"[ " <<o <<" " <<v<<" ]"<< " -> { ";
1038 const PointsTo &ovPts = vPtD->getPts(atKey(o, v));
1039 if (!ovPts.empty())
1040 {
1041 for (NodeID n: ovPts)
1042 {
1043 f << n << " ";
1044 }
1045 }
1046 else
1047 {
1048 f << " ";
1049 }
1050 f << "}\n";
1051 }
1052 }
1053 }
1054
1055 f << "---VERSIONED---\n";
1056
1057 f.close();
1058 if (f.good())
1059 {
1060 SVFUtil::outs() << "\n";
1061 return;
1062 }
1063}
1064
1066{
1067 std::string line;
1068 std::string delimiter1 = " -> { ";
1069 std::string delimiter2 = " }";
1070 while (F.good())
1071 {
1072 // Parse a single line in the form of "[ var version ] -> { obj1 obj2 obj3 }"
1073 getline(F, line);
1074 if (line == "---VERSIONED---") break;
1075 std::string pair = line.substr(line.find("[ ")+1, line.find(" ]"));
1076
1077 // Parse VersionKey
1078 std::istringstream ss(pair);
1079 NodeID nodeID;
1081 ss>> nodeID >> nodeVersion;
1083
1084 // Parse Point-to set
1085 size_t pos = line.find(delimiter1);
1086 if (pos == std::string::npos) break;
1087 if (line.back() != '}') break;
1088 pos = pos + delimiter1.length();
1089 size_t len = line.length() - pos - delimiter2.length();
1090 std::string objs = line.substr(pos, len);
1092 if (!objs.empty())
1093 {
1094 std::istringstream pt(objs);
1095 NodeID obj;
1096 while (pt.good())
1097 {
1098 pt >> obj;
1099 dstPts.set(obj);
1100 }
1101 }
1102
1103 // union point-to reuslt
1104 vPtD->unionPts(keyPair, dstPts);
1105 }
1106
1107}
1108
1109unsigned VersionedFlowSensitive::SCC::detectSCCs(VersionedFlowSensitive *vfs,
1110 const SVFG *svfg, const NodeID object,
1111 const std::vector<const SVFGNode *> &startingNodes,
1112 std::vector<int> &partOf,
1113 std::vector<const IndirectSVFGEdge *> &footprint)
1114{
1115 partOf.resize(svfg->getTotalNodeNum());
1116 std::fill(partOf.begin(), partOf.end(), -1);
1117 footprint.clear();
1118
1119 std::vector<NodeData> nodeData(svfg->getTotalNodeNum(), { -1, -1, false});
1120 std::stack<const SVFGNode *> stack;
1121
1122 int index = 0;
1123 int currentSCC = 0;
1124
1125 for (const SVFGNode *v : startingNodes)
1126 {
1127 if (nodeData[v->getId()].index == -1)
1128 {
1130 }
1131 }
1132
1133 // Make sure footprints with the same edges pass ==/hash the same.
1134 std::sort(footprint.begin(), footprint.end());
1135
1136 return currentSCC;
1137}
1138
1140 const NodeID object,
1141 std::vector<int> &partOf,
1142 std::vector<const IndirectSVFGEdge *> &footprint,
1143 std::vector<NodeData> &nodeData,
1144 std::stack<const SVFGNode *> &stack,
1145 int &index,
1146 int &currentSCC,
1147 const SVFGNode *v)
1148{
1149 const NodeID vId = v->getId();
1150
1151 nodeData[vId].index = index;
1152 nodeData[vId].lowlink = index;
1153 ++index;
1154
1155 stack.push(v);
1156 nodeData[vId].onStack = true;
1157
1158 for (const SVFGEdge *e : v->getOutEdges())
1159 {
1160 const IndirectSVFGEdge *ie = SVFUtil::dyn_cast<IndirectSVFGEdge>(e);
1161 if (!ie) continue;
1162
1163 const SVFGNode *w = ie->getDstNode();
1164 const NodeID wId = w->getId();
1165
1166 // If object is not part of the edge, there is no edge from v to w.
1167 if (!ie->getPointsTo().test(object)) continue;
1168
1169 // Even if we don't count edges to stores and deltas for SCCs' sake, they
1170 // are relevant to the footprint as a propagation still occurs over such edges.
1171 footprint.push_back(ie);
1172
1173 // Ignore edges to delta nodes because they are prelabeled so cannot
1174 // be part of the SCC v is in (already in nodesTodo from the prelabeled set).
1175 // Similarly, store nodes.
1176 if (vfs->delta(wId) || vfs->isStore(wId)) continue;
1177
1178 if (nodeData[wId].index == -1)
1179 {
1180 visit(vfs, object, partOf, footprint, nodeData, stack, index, currentSCC, w);
1181 nodeData[vId].lowlink = std::min(nodeData[vId].lowlink, nodeData[wId].lowlink);
1182 }
1183 else if (nodeData[wId].onStack)
1184 {
1185 nodeData[vId].lowlink = std::min(nodeData[vId].lowlink, nodeData[wId].index);
1186 }
1187 }
1188
1189 if (nodeData[vId].lowlink == nodeData[vId].index)
1190 {
1191 const SVFGNode *w = nullptr;
1192 do
1193 {
1194 w = stack.top();
1195 stack.pop();
1196 const NodeID wId = w->getId();
1197 nodeData[wId].onStack = false;
1199 }
1200 while (w != v);
1201
1202 // For the next SCC.
1203 ++currentSCC;
1204 }
1205}
#define F(f)
#define TIMEINTERVAL
Definition SVFType.h:512
set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) add_llvm_executable(wpa wpa.cpp) target_link_libraries(wpa PUBLIC $
Definition CMakeLists.txt:1
cJSON * p
Definition cJSON.cpp:2559
return(char *) p.buffer
newitem type
Definition cJSON.cpp:2739
cJSON * a
Definition cJSON.cpp:2560
cJSON * n
Definition cJSON.cpp:2558
const cJSON *const b
Definition cJSON.h:255
int index
Definition cJSON.h:170
virtual const PointsTo & getPts(NodeID id)
Operation of points-to set.
Definition Andersen.h:239
VersionedPTDataTy * getVersionedPTDataTy() const
virtual void writeToFile(const std::string &filename)
Interface for analysis result storage on filesystem.
virtual void writeObjVarToFile(const std::string &filename)
virtual void readAndSetObjFieldSensitivity(std::ifstream &f, const std::string &delimiterStr)
const PointsTo & getPts(NodeID id) override
virtual void readPtsResultFromFile(std::ifstream &f)
virtual void readGepObjVarMapFromFile(std::ifstream &f)
const_iterator end(void) const
bool push(const Data &data)
Definition WorkList.h:165
bool empty() const
Definition WorkList.h:146
virtual void solveConstraints()
bool isStrongUpdate(const SVFGNode *node, NodeID &singleton)
Return TRUE if this is a strong update STORE statement.
AndersenWaveDiff * ander
SVFG::SVFGEdgeSetTy SVFGEdgeSetTy
double storeTime
time of store edges
void finalize() override
Finalize analysis.
bool processSVFGNode(SVFGNode *node)
double loadTime
time of load edges
bool updateCallGraph(const CallSiteToFunPtrMap &callsites) override
Update call graph.
void initialize() override
Initialize analysis.
std::vector< std::pair< hclust_fast_methods, std::vector< NodeID > > > candidateMappings
Save candidate mappings for evaluation's sake.
double updateTime
time of strong/weak updates.
iterator begin()
Iterators.
IDToNodeMapTy::const_iterator const_iterator
u32_t getTotalNodeNum() const
Get total number of node/edge.
IDToNodeMapTy::iterator iterator
Node Iterators.
const GEdgeSetTy & getInEdges() const
const NodeBS & getPointsTo() const
Return points-to of the MR.
Definition SVFGNode.h:52
static std::vector< NodeID > cluster(BVDataPTAImpl *pta, const std::vector< std::pair< NodeID, unsigned > > keys, std::vector< std::pair< hclust_fast_methods, std::vector< NodeID > > > &candidates, std::string evalSubtitle="")
static std::vector< NodeID > getReverseNodeMapping(const std::vector< NodeID > &nodeMapping)
static Option< bool > OPTSVFG
Definition Options.h:149
static const Option< bool > PredictPtOcc
Definition Options.h:66
static const Option< u32_t > VersioningThreads
Number of threads for the versioning phase.
Definition Options.h:78
Set< const CallICFGNode * > CallInstSet
void getIndCallSitesInvokingCallee(const SVFFunction *callee, PTACallGraphEdge::CallInstSet &csSet)
PTATY
Pointer analysis type list.
bool isFieldInsensitive(NodeID id) const
PTAStat * stat
Statistics.
PTACallGraph * getCallGraph() const
Return call graph.
static SVFIR * pag
SVFIR.
virtual const NodeBS & getAllFieldsObjVars(NodeID id)
std::shared_ptr< std::vector< NodeID > > MappingPtr
Definition PointsTo.h:42
static void setCurrentBestNodeMapping(MappingPtr newCurrentBestNodeMapping, MappingPtr newCurrentBestReverseNodeMapping)
Definition PointsTo.cpp:371
u32_t count() const
Returns number of elements.
Definition PointsTo.cpp:111
void visit(NodeID v)
Definition SCC.h:249
NodeID getId() const
Get ID.
SVFGNode * getSVFGNode(NodeID id) const
Get a SVFG node.
Definition SVFG.h:150
const DummyVersionPropSVFGNode * addDummyVersionPropSVFGNode(const NodeID object, const NodeID version)
Definition SVFG.h:263
void removeSVFGEdge(SVFGEdge *edge)
Remove a SVFG edge.
Definition SVFG.h:238
const CallICFGNode * isCallSiteRetSVFGNode(const SVFGNode *node) const
Whether a node is callsite return SVFGNode.
Definition SVFG.cpp:732
const SVFFunction * isFunEntrySVFGNode(const SVFGNode *node) const
Whether a node is function entry SVFGNode.
Definition SVFG.cpp:706
bool isConstantObj(NodeID id) const
Definition SVFIR.h:465
const CallSiteToFunPtrMap & getIndirectCallsites() const
Add/get indirect callsites.
Definition SVFIR.h:351
const MemObj * getObject(NodeID id) const
Definition SVFIR.h:396
static double getClk(bool mark=false)
Definition SVFStat.cpp:48
virtual bool isPointer() const
Whether it is a pointer.
void set(unsigned Idx)
unsigned count() const
void reset(unsigned Idx)
NodeID getPAGDstNodeID() const
Definition VFGNode.h:157
PAGNode * getPAGSrcNode() const
Definition VFGNode.h:162
PAGNode * getPAGDstNode() const
Definition VFGNode.h:167
NodeID getPAGSrcNodeID() const
Definition VFGNode.h:152
static void visit(VersionedFlowSensitive *vfs, const NodeID object, std::vector< int > &partOf, std::vector< const IndirectSVFGEdge * > &footprint, std::vector< NodeData > &nodeData, std::stack< const SVFGNode * > &stack, int &index, int &currentSCC, const SVFGNode *v)
Called by detectSCCs then called recursively.
Version getYield(const NodeID l, const NodeID o) const
Returns the yielded version of o at l. If no such version exists, returns invalidVersion.
void dumpLocVersionMaps(void) const
Dumps maps consume and yield.
BVDataPTAImpl::VersionedPTDataTy * vPtD
Points-to DS for working with versions.
LocVersionMap yield
Actual yield map. Yield analogue to consume.
std::vector< bool > isStoreMap
isStoreMap[l] means SVFG node l is a store node.
virtual void updateConnectedNodes(const SVFGEdgeSetTy &newEdges) override
Update nodes connected during updating call graph.
virtual bool processLoad(const LoadSVFGNode *load) override
static bool meld(MeldVersion &mv1, const MeldVersion &mv2)
Melds v2 into v1 (in place), returns whether a change occurred.
VersionRelianceMap versionReliance
o -> (version -> versions which rely on it).
u32_t numPrelabelVersions
Number of versions created during prelabeling.
void removeAllIndirectSVFGEdges(void)
Removes all indirect edges in the SVFG.
Map< NodeID, NodeID > equivalentObject
void readVersionedAnalysisResultFromFile(std::ifstream &F)
virtual void buildDeltaMaps(void)
Fills in deltaMap and deltaSourceMap for the SVFG.
NodeBS & getStmtReliance(const NodeID o, const Version v)
Returns the statements which rely on o:v.
static const Version invalidVersion
If this version appears, there has been an error.
virtual bool deltaSource(const NodeID l) const
double meldLabelingTime
Time to meld label SVFG.
static VersionedFlowSensitive * vfspta
static VersionedVar atKey(NodeID, Version)
Return key into vPtD for address-taken var of a specific version.
Version getVersion(const NodeID l, const NodeID o, const LocVersionMap &lvm) const
Shared code for getConsume and getYield. They wrap this function.
std::vector< bool > isLoadMap
isLoadMap[l] means SVFG node l is a load node.
static void dumpMeldVersion(MeldVersion &v)
Dumps a MeldVersion to stdout.
double prelabelingTime
Time to prelabel SVFG.
void propagateVersion(NodeID o, Version v)
void prelabel(void)
Prelabel the SVFG: set y(o) for stores and c(o) for delta nodes to a new version.
virtual void initialize() override
Initialize analysis.
virtual void processNode(NodeID n) override
Handle various constraints.
virtual void buildIsStoreLoadMaps(void)
Fills in isStoreMap and isLoadMap.
virtual bool isLoad(const NodeID l) const
Returns true if l is a load node.
void dumpReliances(void) const
Dumps versionReliance and stmtReliance.
virtual bool delta(const NodeID l) const
std::vector< ObjToVersionMap > LocVersionMap
virtual bool processStore(const StoreSVFGNode *store) override
void setConsume(const NodeID l, const NodeID o, const Version v)
Sets the consumed version of o at l to v.
virtual bool isStore(const NodeID l) const
Returns true if l is a store node.
void meldLabel(void)
Meld label the prelabeled SVFG.
void writeVersionedAnalysisResultToFile(const std::string &filename)
VersionedFlowSensitive(SVFIR *_pag, PTATY type=VFS_WPA)
Constructor.
void setYield(const NodeID l, const NodeID o, const Version v)
Sets the yielded version of o at l to v.
void solveAndwritePtsToFile(const std::string &filename) override
virtual void finalize() override
Finalize analysis.
void setVersion(const NodeID l, const NodeID o, const Version v, LocVersionMap &lvm)
Shared code for setConsume and setYield. They wrap this function.
std::vector< Version > & getReliantVersions(const NodeID o, const Version v)
Returns the versions of o which rely on o:v.
void readPtsFromFile(const std::string &filename) override
virtual void cluster(void) override
Override since we want to assign different weights based on versioning.
Map< NodeID, Map< Version, NodeBS > > stmtReliance
o x version -> statement nodes which rely on that o/version.
double versionPropTime
Time to propagate versions to versions which rely on them.
Version getConsume(const NodeID l, const NodeID o) const
Returns the consumed version of o at l. If no such version exists, returns invalidVersion.
Map< NodeID, Version > ObjToVersionMap
u32_t numPrelabeledNodes
Additional statistics.
std::unique_ptr< SCC > scc
SCC.
Definition WPASolver.h:193
virtual void pushIntoWorklist(NodeID id)
Definition WPASolver.h:156
virtual void propagate(GNODE *v)
Definition WPASolver.h:127
void setGraph(GraphType g)
Definition WPASolver.h:78
std::ostream & outs()
Overwrite llvm::outs()
Definition SVFUtil.h:50
for isBitcode
Definition BasicTypes.h:68
std::pair< NodeID, Version > VersionedVar
u32_t NodeID
Definition GeneralType.h:55
std::unordered_map< Key, Value, Hash, KeyEqual, Allocator > Map
llvm::IRBuilder IRBuilder
Definition BasicTypes.h:74
unsigned Version
unsigned u32_t
Definition GeneralType.h:46