Static Value-Flow Analysis
Loading...
Searching...
No Matches
PersistentPointsToCache.h
Go to the documentation of this file.
1//===- PersistentPointsToCache.h -- Persistent points-to sets ----------------//
2
3/*
4 * PersistentPointsToCache.h
5 *
6 * The implementation is based on
7 * Mohamad Barbar and Yulei Sui. Hash Consed Points-To Sets.
8 * 28th Static Analysis Symposium (SAS'21)
9 *
10 * Created on: Sep 28, 2020
11 * Author: Mohamad Barbar
12 */
13
14#ifndef PERSISTENT_POINTS_TO_H_
15#define PERSISTENT_POINTS_TO_H_
16
17#include <iomanip>
18#include <iostream>
19#include <vector>
20#include <functional>
21
22#include "SVFIR/SVFType.h"
23
24namespace SVF
25{
26
31template <typename Data>
33{
34public:
36 typedef std::function<Data(const Data &, const Data &)> DataOp;
37 // TODO: an unordered pair type may be better.
39
41 {
42 return 0;
43 };
44
45public:
47 {
48 idToPts.push_back(std::make_unique<Data>());
50
51 initStats();
52 }
53
55 void clear()
56 {
57 idToPts.clear();
58 ptsToId.clear();
59
60 unionCache.clear();
61 complementCache.clear();
62 intersectionCache.clear();
63 }
64
66 void reset(void)
67 {
68 clear();
69
70 // Put the empty data back in.
72 idToPts.push_back(std::make_unique<Data>());
73
74 idCounter = 1;
75 // Cache is empty...
76 initStats();
77 }
78
80 void remapAllPts(void)
81 {
82 for (auto &d : idToPts) d->checkAndRemap();
83
84 // Rebuild ptsToId from idToPts.
85 ptsToId.clear();
86 for (PointsToID i = 0; i < idToPts.size(); ++i) ptsToId[*idToPts[i]] = i;
87 }
88
92 {
93 // Is it already in the cache?
94 typename PTSToIDMap::const_iterator foundId = ptsToId.find(pts);
95 if (foundId != ptsToId.end()) return foundId->second;
96
97 // Otherwise, insert it.
99 idToPts.push_back(std::make_unique<Data>(pts));
100 ptsToId[pts] = id;
101
102 return id;
103 }
104
106 const Data &getActualPts(PointsToID id) const
107 {
108 // Check if the points-to set for ID has already been stored.
109 assert(idToPts.size() > id && "PPTC::getActualPts: points-to set not stored!");
110 return *idToPts.at(id);
111 }
112
115 {
116 static const DataOp unionOp = [](const Data &lhs, const Data &rhs)
117 {
118 return lhs | rhs;
119 };
120
121 ++totalUnions;
122
123 // Order operands so we don't perform x U y and y U x separately.
124 std::pair<PointsToID, PointsToID> operands = std::minmax(lhs, rhs);
125
126 // Property cases.
127 // EMPTY_SET U x
128 if (operands.first == emptyPointsToId())
129 {
131 return operands.second;
132 }
133
134 // x U x
135 if (operands.first == operands.second)
136 {
138 return operands.first;
139 }
140
141 bool opPerformed = false;
143
144 if (opPerformed)
145 {
146 ++uniqueUnions;
147
148 // We can use lhs/rhs here rather than our ordered operands,
149 // because the operation was commutative.
150
151 // if x U y = z, then x U z = z,
152 if (lhs != result)
153 {
154 unionCache[std::minmax(lhs, result)] = result;
156 ++totalUnions;
157 }
158
159 // and y U z = z.
160 if (rhs != result)
161 {
162 unionCache[std::minmax(rhs, result)] = result;
164 ++totalUnions;
165 }
166 }
167 else ++lookupUnions;
168
169 return result;
170 }
171
174 {
175 static const DataOp complementOp = [](const Data &lhs, const Data &rhs)
176 {
177 return lhs - rhs;
178 };
179
181
182 // Property cases.
183 // x - x
184 if (lhs == rhs)
185 {
187 return emptyPointsToId();
188 }
189
190 // x - EMPTY_SET
191 if (rhs == emptyPointsToId())
192 {
194 return lhs;
195 }
196
197 // EMPTY_SET - x
198 if (lhs == emptyPointsToId())
199 {
201 return emptyPointsToId();
202 }
203
204 bool opPerformed = false;
206
207 if (opPerformed)
208 {
210
211 // We performed lhs - rhs = result, so...
212 if (result != emptyPointsToId())
213 {
214 // result AND rhs = EMPTY_SET,
215 intersectionCache[std::minmax(result, rhs)] = emptyPointsToId();
218
219 // and result AND lhs = result,
220 intersectionCache[std::minmax(result, lhs)] = result;
223
224 // and result - rhs = result.
225 complementCache[std::make_pair(result, rhs)] = result;
228 }
229 }
230 else ++lookupComplements;
231
232 return result;
233 }
234
237 {
238 static const DataOp intersectionOp = [](const Data &lhs, const Data &rhs)
239 {
240 return lhs & rhs;
241 };
242
244
245 // Order operands so we don't perform x U y and y U x separately.
246 std::pair<PointsToID, PointsToID> operands = std::minmax(lhs, rhs);
247
248 // Property cases.
249 // EMPTY_SET & x
250 if (operands.first == emptyPointsToId())
251 {
253 return emptyPointsToId();
254 }
255
256 // x & x
257 if (operands.first == operands.second)
258 {
260 return operands.first;
261 }
262
263 bool opPerformed = false;
265 if (opPerformed)
266 {
268
269 // When the result is empty, we won't be adding anything of substance.
270 if (result != emptyPointsToId())
271 {
272 // We performed lhs AND rhs = result, so...
273 // result AND rhs = result,
274 if (result != rhs)
275 {
276 intersectionCache[std::minmax(result, rhs)] = result;
279 }
280
281 // and result AND lhs = result,
282 if (result != lhs)
283 {
284 intersectionCache[std::minmax(result, lhs)] = result;
287 }
288
289 // Also (thanks reviewer #2)
290 // result U lhs = result,
291 if (result != emptyPointsToId() && result != lhs)
292 {
293 unionCache[std::minmax(lhs, result)] = lhs;
295 ++totalUnions;
296 }
297
298 // And result U rhs = rhs.
299 if (result != emptyPointsToId() && result != rhs)
300 {
301 unionCache[std::minmax(rhs, result)] = rhs;
303 ++totalUnions;
304 }
305 }
306 }
307 else ++lookupIntersections;
308
309 return result;
310 }
311
313 void printStats(const std::string subtitle) const
314 {
315 static const unsigned fieldWidth = 25;
316 SVFUtil::outs().flags(std::ios::left);
317
318 SVFUtil::outs() << std::setw(fieldWidth) << "UniquePointsToSets" << idToPts.size() << "\n";
319
320 SVFUtil::outs() << std::setw(fieldWidth) << "TotalUnions" << totalUnions << "\n";
321 SVFUtil::outs() << std::setw(fieldWidth) << "PropertyUnions" << propertyUnions << "\n";
322 SVFUtil::outs() << std::setw(fieldWidth) << "UniqueUnions" << uniqueUnions << "\n";
323 SVFUtil::outs() << std::setw(fieldWidth) << "LookupUnions" << lookupUnions << "\n";
324 SVFUtil::outs() << std::setw(fieldWidth) << "PreemptiveUnions" << preemptiveUnions << "\n";
325
326 SVFUtil::outs() << std::setw(fieldWidth) << "TotalComplements" << totalComplements << "\n";
327 SVFUtil::outs() << std::setw(fieldWidth) << "PropertyComplements" << propertyComplements << "\n";
328 SVFUtil::outs() << std::setw(fieldWidth) << "UniqueComplements" << uniqueComplements << "\n";
329 SVFUtil::outs() << std::setw(fieldWidth) << "LookupComplements" << lookupComplements << "\n";
330 SVFUtil::outs() << std::setw(fieldWidth) << "PreemptiveComplements" << preemptiveComplements << "\n";
331
332 SVFUtil::outs() << std::setw(fieldWidth) << "TotalIntersections" << totalIntersections << "\n";
333 SVFUtil::outs() << std::setw(fieldWidth) << "PropertyIntersections" << propertyIntersections << "\n";
334 SVFUtil::outs() << std::setw(fieldWidth) << "UniqueIntersections" << uniqueIntersections << "\n";
335 SVFUtil::outs() << std::setw(fieldWidth) << "LookupIntersections" << lookupIntersections << "\n";
336 SVFUtil::outs() << std::setw(fieldWidth) << "PreemptiveIntersections" << preemptiveIntersections << "\n";
337
338 SVFUtil::outs().flush();
339 }
340
346 {
348 for (const auto &d : idToPts) allPts[*d] = 1;
349 return allPts;
350 }
351
352 // TODO: ref count API for garbage collection.
353
354private:
356 {
357 // Make sure we don't overflow.
358 assert(idCounter != emptyPointsToId() && "PPTC::newPointsToId: PointsToIDs exhausted! Try a larger type.");
359 return idCounter++;
360 }
361
366 bool commutative, bool &opPerformed)
367 {
368 std::pair<PointsToID, PointsToID> operands;
369 // If we're commutative, we want to always perform the same operation: x op y.
370 // Performing x op y sometimes and y op x other times is a waste of time.
371 if (commutative) operands = std::minmax(lhs, rhs);
372 else operands = std::make_pair(lhs, rhs);
373
374 // Check if we have performed this operation
375 OpCache::const_iterator foundResult = opCache.find(operands);
376 if (foundResult != opCache.end()) return foundResult->second;
377
378 opPerformed = true;
379
380 const Data &lhsPts = getActualPts(lhs);
381 const Data &rhsPts = getActualPts(rhs);
382
384
386 // Intern points-to set: check if result already exists.
387 typename PTSToIDMap::const_iterator foundId = ptsToId.find(result);
388 if (foundId != ptsToId.end()) resultId = foundId->second;
389 else
390 {
392 idToPts.push_back(std::make_unique<Data>(result));
394 }
395
396 // Cache the result, for hash-consing.
398
399 return resultId;
400 }
401
403 inline void initStats(void)
404 {
405
406 totalUnions = 0;
407 uniqueUnions = 0;
408 propertyUnions = 0;
409 lookupUnions = 0;
421 }
422
423private:
429 std::vector<std::unique_ptr<Data>> idToPts;
432
439
442
443 // Statistics:
459};
460
461} // End namespace SVF
462
463#endif /* PERSISTENT_POINTS_TO_H_ */
PointsToID unionPts(PointsToID lhs, PointsToID rhs)
Unions lhs and rhs and returns their union's ID.
PointsToID intersectPts(PointsToID lhs, PointsToID rhs)
Intersects lhs and rhs (lhs AND rhs) and returns the intersection's ID.
std::vector< std::unique_ptr< Data > > idToPts
OpCache intersectionCache
Maps two IDs to their intersection. Keys must be sorted.
std::function< Data(const Data &, const Data &)> DataOp
PTSToIDMap ptsToId
Maps points-to sets to their corresponding ID.
OpCache unionCache
Maps two IDs to their union. Keys must be sorted.
Map< Data, unsigned > getAllPts(void)
const Data & getActualPts(PointsToID id) const
Returns the points-to set which id represents. id must be stored in the cache.
void printStats(const std::string subtitle) const
Print statistics on operations and points-to set numbers.
PointsToID complementPts(PointsToID lhs, PointsToID rhs)
Relatively complements lhs and rhs (lhs \ rhs) and returns it's ID.
Map< std::pair< PointsToID, PointsToID >, PointsToID > OpCache
void remapAllPts(void)
Remaps all points-to sets stored in the cache to the current mapping.
static PointsToID emptyPointsToId(void)
PointsToID emplacePts(const Data &pts)
void initStats(void)
Initialises statistics variables to 0.
PointsToID opPts(PointsToID lhs, PointsToID rhs, const DataOp &dataOp, OpCache &opCache, bool commutative, bool &opPerformed)
PointsToID idCounter
Used to generate new PointsToIDs. Any non-zero is valid.
OpCache complementCache
Maps two IDs to their relative complement.
void reset(void)
Resets the cache removing everything except the emptyData it was initialised with.
std::ostream & outs()
Overwrite llvm::outs()
Definition SVFUtil.h:50
for isBitcode
Definition BasicTypes.h:68
unsigned long long u64_t
Definition GeneralType.h:48
llvm::IRBuilder IRBuilder
Definition BasicTypes.h:74
unsigned PointsToID
Definition GeneralType.h:63