@@ -18,7 +18,7 @@ namespace rir {
1818namespace pir {
1919
2020/*
21- * Generic implementation of a (forward) static analysis.
21+ * Generic implementation of a static analysis.
2222 *
2323 * In "mergepoint" we keep a list of abstract states for every basic block. The
2424 * first state is the abstract state at the beginning of the basic block. We
@@ -27,7 +27,7 @@ namespace pir {
2727 * To implement a concrete static analysis, the "apply" method needs to be
2828 * implemented, which supplies the implementation for every instruction. Apply
2929 * is supposed to modify the abstract state, but not (!) the analysis itself
30- * (that is why it is marked const). The reason is, that after we reached a
30+ * (that is why it is marked const). The reason is that after we reach a
3131 * fixed-point, it should be possible to reconstruct the state of the analysis
3232 * at every instruction. To do so, a dominating state is loaded from
3333 * "mergepoint" and then "apply" is used to seek to the desired instruction
@@ -38,8 +38,6 @@ namespace pir {
3838 * provided by the subclass that specializes StaticAnalysis.
3939 */
4040
41- class AvailableCheckpoints ;
42-
4341enum class AnalysisDebugLevel {
4442 None,
4543 Taint,
@@ -69,8 +67,10 @@ class StaticAnalysis {
6967
7068 struct BBSnapshot {
7169 bool seen = false ;
72- size_t incomming = 0 ;
70+ size_t incoming = 0 ;
71+ // entry stores the state *before* the first instruction in the BB
7372 AbstractState entry;
73+ // extra stores the state *after* calling apply on the given instruction
7474 std::unordered_map<Instruction*, AbstractState> extra;
7575 };
7676 typedef std::vector<BBSnapshot> AnalysisSnapshots;
@@ -83,24 +83,33 @@ class StaticAnalysis {
8383 // For lookup, after fixed-point was found
8484 virtual AbstractResult apply (AbstractState&, Instruction*) const = 0;
8585
86- constexpr static size_t MAX_CACHE_SIZE = 128 / sizeof (AbstractState);
87-
88- std::unordered_map<Instruction*, AbstractState> cache;
89- std::deque<Instruction*> cacheQueue;
90- void addToCache (Instruction* i, const AbstractState& state) const {
86+ #ifdef PIR_ANALYSIS_USE_LOOKUP_CACHE
87+ constexpr static size_t MAX_CACHE_SIZE =
88+ std::max (1UL , 128 / sizeof (AbstractState));
89+
90+ mutable std::unordered_map<Instruction*, AbstractState> beforeCache;
91+ mutable std::unordered_map<Instruction*, AbstractState> afterCache;
92+ mutable std::deque<Instruction*> beforeCacheQueue;
93+ mutable std::deque<Instruction*> afterCacheQueue;
94+ void addToCache (PositioningStyle pos, Instruction* i,
95+ const AbstractState& state) const {
96+ auto & cache = pos == BeforeInstruction ? beforeCache : afterCache;
97+ auto & cacheQueue =
98+ pos == BeforeInstruction ? beforeCacheQueue : afterCacheQueue;
9199 if (cache.count (i)) {
92- const_cast <StaticAnalysis*>( this )-> cache .erase (cache.find (i));
93- const_cast <StaticAnalysis*>( this )-> cache .emplace (i, state);
100+ cache.erase (cache.find (i));
101+ cache.emplace (i, state);
94102 return ;
95103 }
96104 if (cacheQueue.size () > MAX_CACHE_SIZE) {
97105 auto oldest = cacheQueue.front ();
98- const_cast <StaticAnalysis*>( this )-> cacheQueue .pop_front ();
99- const_cast <StaticAnalysis*>( this )-> cache .erase (cache.find (oldest));
106+ cacheQueue.pop_front ();
107+ cache.erase (cache.find (oldest));
100108 }
101- const_cast <StaticAnalysis*>( this )-> cache .emplace (i, state);
102- const_cast <StaticAnalysis*>( this )-> cacheQueue .push_back (i);
109+ cache.emplace (i, state);
110+ cacheQueue.push_back (i);
103111 }
112+ #endif
104113 std::unordered_map<BB*, AbstractState> exitpoints;
105114 AbstractState exitpoint;
106115
@@ -230,21 +239,23 @@ class StaticAnalysis {
230239 return at<PositioningStyle::AfterInstruction>(i);
231240 }
232241
242+ private:
233243 template <PositioningStyle POS>
234244 AbstractState at (Instruction* i) const {
235245 if (!done)
236246 const_cast <StaticAnalysis*>(this )->operator ()();
237247 assert (done);
238248
239- BB* bb = i->bb ();
240-
241- if (cache.count (i)) {
242- auto state = cache.at (i);
243- if (PositioningStyle::AfterInstruction == POS)
244- apply (state, i);
245- return state;
249+ #ifdef PIR_ANALYSIS_USE_LOOKUP_CACHE
250+ if (beforeCache.count (i) && POS == BeforeInstruction) {
251+ return beforeCache.at (i);
252+ }
253+ if (afterCache.count (i) && POS == AfterInstruction) {
254+ return afterCache.at (i);
246255 }
256+ #endif
247257
258+ BB* bb = i->bb ();
248259 if (Forward)
249260 return findSnapshot<POS>(bb->begin (), bb->end (), bb, i);
250261
@@ -254,12 +265,13 @@ class StaticAnalysis {
254265 template <PositioningStyle POS, typename Iter>
255266 AbstractState findSnapshot (Iter begin, Iter end, BB* bb,
256267 Instruction* i) const {
257- size_t tried = 0 ;
258268 const BBSnapshot& bbSnapshots = snapshots[bb->id ];
259269
270+ // Find the snapshot closest to the desired state
271+ size_t tried = 0 ;
260272 auto snapshotPos = begin;
261- for (auto pos = begin, e = end;
262- pos != e && tried < bbSnapshots. extra . size (); ++pos) {
273+ for (auto pos = begin; pos ! = end && tried < bbSnapshots. extra . size () ;
274+ ++pos) {
263275 if (POS == BeforeInstruction && i == *pos)
264276 break ;
265277 if (bbSnapshots.extra .count (*pos)) {
@@ -270,18 +282,43 @@ class StaticAnalysis {
270282 break ;
271283 }
272284
285+ auto state =
286+ tried == 0 ? bbSnapshots.entry : bbSnapshots.extra .at (*snapshotPos);
287+
288+ // If we found a snapshot in extra, this gives us the state *after*
289+ // applying, hence we either found the result or need to move to the
290+ // next instruction
291+ if (tried) {
292+ if (i == *snapshotPos) {
293+ assert (POS == AfterInstruction);
294+ #ifdef PIR_ANALYSIS_USE_LOOKUP_CACHE
295+ addToCache (AfterInstruction, i, state);
296+ if (snapshotPos + 1 != end)
297+ addToCache (BeforeInstruction, *(snapshotPos + 1 ), state);
298+ #endif
299+ return state;
300+ }
301+ ++snapshotPos;
302+ assert (snapshotPos != end);
303+ }
304+
273305 // Apply until we arrive at the position
274- auto state = snapshotPos == begin ? bbSnapshots.entry
275- : bbSnapshots.extra .at (*snapshotPos);
276- for (auto pos = snapshotPos, e = end; pos != e; ++pos) {
306+ for (auto pos = snapshotPos; pos != end; ++pos) {
277307 if (POS == BeforeInstruction && i == *pos) {
278- addToCache (i, state);
308+ #ifdef PIR_ANALYSIS_USE_LOOKUP_CACHE
309+ addToCache (BeforeInstruction, i, state);
310+ if (pos != begin)
311+ addToCache (AfterInstruction, *(pos - 1 ), state);
312+ #endif
279313 return state;
280314 }
281315 apply (state, *pos);
282316 if (POS == AfterInstruction && i == *pos) {
317+ #ifdef PIR_ANALYSIS_USE_LOOKUP_CACHE
318+ addToCache (AfterInstruction, i, state);
283319 if (pos + 1 != end)
284- addToCache (*(pos + 1 ), state);
320+ addToCache (BeforeInstruction, *(pos + 1 ), state);
321+ #endif
285322 return state;
286323 }
287324 }
@@ -290,6 +327,7 @@ class StaticAnalysis {
290327 return AbstractState ();
291328 }
292329
330+ public:
293331 typedef std::function<void (const AbstractState&, Instruction*)> Collect;
294332
295333 template <PositioningStyle POS>
@@ -446,14 +484,14 @@ class StaticAnalysis {
446484 if (!thisState.seen ) {
447485 thisState.entry = state;
448486 thisState.seen = true ;
449- thisState.incomming = in->id ;
487+ thisState.incoming = in->id ;
450488 done = false ;
451489 changed[id] = true ;
452- } else if (in->id == thisState.incomming ) {
490+ } else if (in->id == thisState.incoming ) {
453491 thisState.entry = state;
454492 changed[id] = changed[in->id ];
455493 } else {
456- thisState.incomming = -1 ;
494+ thisState.incoming = -1 ;
457495 AbstractState old;
458496 if (DEBUG_LEVEL >= AnalysisDebugLevel::Taint) {
459497 old = thisState.entry ;
0 commit comments