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