Skip to content

Commit 89c54ee

Browse files
committed
Try avoiding unnecessary analysis state recomputation
* The state would be recomputed even with the keepSnapshot flag * (experimental) Disable the lookup cache
1 parent 171b410 commit 89c54ee

File tree

9 files changed

+86
-60
lines changed

9 files changed

+86
-60
lines changed

rir/src/compiler/analysis/available_checkpoints.h

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,7 @@ class FwdAvailableCheckpoints
3737
return AvailableCheckpointsApply::apply(state, i);
3838
}
3939

40-
Checkpoint* reaching(Instruction* i) const {
41-
return StaticAnalysis::at<PositioningStyle::BeforeInstruction>(i).get();
42-
}
40+
Checkpoint* reaching(Instruction* i) const { return before(i).get(); }
4341
};
4442

4543
class RwdAvailableCheckpoints
@@ -53,12 +51,8 @@ class RwdAvailableCheckpoints
5351
return AvailableCheckpointsApply::apply(state, i);
5452
}
5553

56-
Checkpoint* reachingThrough(Instruction* i) const {
57-
return StaticAnalysis::at<PositioningStyle::AfterInstruction>(i).get();
58-
}
59-
Checkpoint* reaching(Instruction* i) const {
60-
return StaticAnalysis::at<PositioningStyle::BeforeInstruction>(i).get();
61-
}
54+
Checkpoint* reachingThrough(Instruction* i) const { return after(i).get(); }
55+
Checkpoint* reaching(Instruction* i) const { return before(i).get(); }
6256
};
6357

6458
class AvailableCheckpoints {

rir/src/compiler/analysis/dead_store.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,7 @@ class DeadStoreAnalysis {
7272
: StaticAnalysis("envLeak", cls, code, initialState, NULL, log),
7373
promEnv(promEnv) {}
7474

75-
EnvSet leakedWhile(Instruction* i) const {
76-
return at<StaticAnalysis::PositioningStyle::AfterInstruction>(i);
77-
}
75+
EnvSet leakedWhile(Instruction* i) const { return after(i); }
7876

7977
protected:
8078
AbstractResult apply(EnvSet& state, Instruction* i) const override {
@@ -361,7 +359,7 @@ class DeadStoreAnalysis {
361359

362360
public:
363361
bool isObserved(StVar* st) const {
364-
auto state = at<PositioningStyle::BeforeInstruction>(st);
362+
auto state = before(st);
365363
auto e = resolveEnv(st->env());
366364
Variable var({st->varName, e});
367365
if (state.ignoreStore.count(var))
@@ -373,7 +371,7 @@ class DeadStoreAnalysis {
373371
}
374372

375373
bool isObservedOnlyByDeopt(StVar* st) const {
376-
auto state = at<PositioningStyle::BeforeInstruction>(st);
374+
auto state = before(st);
377375
auto e = resolveEnv(st->env());
378376
Variable var({st->varName, e});
379377
assert(!(state.completelyObserved.count(e) &&
@@ -385,7 +383,7 @@ class DeadStoreAnalysis {
385383

386384
std::unordered_set<Instruction*>
387385
observedByDeoptInstructions(StVar* st) const {
388-
auto state = at<PositioningStyle::BeforeInstruction>(st);
386+
auto state = before(st);
389387
auto e = resolveEnv(st->env());
390388
assert(state.observedByDeopt.count(e));
391389
return state.observedByDeopt.at(e);

rir/src/compiler/analysis/generic_static_analysis.h

Lines changed: 72 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace rir {
1818
namespace 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-
4341
enum 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;

rir/src/compiler/analysis/last_env.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,7 @@ class LastEnv : public StaticAnalysis<AbstractUnique<Value>> {
3737

3838
bool envStillValid(Instruction* i) { return currentEnv(i) == i->env(); }
3939

40-
Value* currentEnv(Instruction* i) {
41-
return StaticAnalysis::at<PositioningStyle::BeforeInstruction>(i).get();
42-
}
40+
Value* currentEnv(Instruction* i) { return before(i).get(); }
4341
};
4442

4543
} // namespace pir

rir/src/compiler/analysis/unnecessary_contexts.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ class UnnecessaryContexts : public StaticAnalysis<UnnecessaryContextsState> {
106106
};
107107

108108
PushContext* canRemove(PopContext* i) const {
109-
auto res = StaticAnalysis::at<PositioningStyle::BeforeInstruction>(i);
109+
auto res = before(i);
110110
if (res.get() && res.get() == i->push() && !res.needed)
111111
return res.get();
112112
return nullptr;

rir/src/compiler/native/lower_function_llvm.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,7 @@ void LowerFunctionLLVM::compilePushContext(Instruction* i) {
663663
didLongjmp);
664664
}
665665

666-
// Handle Incomming longjumps
666+
// Handle incoming longjumps
667667
{
668668
builder.SetInsertPoint(didLongjmp);
669669
llvm::Value* returned = builder.CreateLoad(

rir/src/compiler/opt/assumptions.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,7 @@ struct AvailableAssumptions
169169
return res;
170170
}
171171
const SmallSet<AAssumption> at(Instruction* i) const {
172-
auto res = StaticAnalysis::at<
173-
StaticAnalysis::PositioningStyle::BeforeInstruction>(i);
172+
auto res = before(i);
174173
return res.available;
175174
}
176175
};

rir/src/compiler/opt/load_elision.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,7 @@ struct AvailableLoads : public StaticAnalysis<IntersectionSet<ALoad>> {
8282
}
8383

8484
ALoad get(Instruction* i) const {
85-
auto res = StaticAnalysis::at<
86-
StaticAnalysis::PositioningStyle::BeforeInstruction>(i);
85+
auto res = before(i);
8786
for (auto dld : res.available) {
8887
if (dld.same(i))
8988
return dld;

rir/src/compiler/opt/scope_resolution.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,8 +255,8 @@ bool ScopeResolution::apply(Compiler&, ClosureVersion* cls, Code* code,
255255
Instruction* i = *ip;
256256
auto next = ip + 1;
257257

258-
auto before = analysis.at<ScopeAnalysis::BeforeInstruction>(i);
259-
auto after = analysis.at<ScopeAnalysis::AfterInstruction>(i);
258+
auto before = analysis.before(i);
259+
auto after = analysis.after(i);
260260

261261
// Force and callees can only see our env only through
262262
// reflection

0 commit comments

Comments
 (0)