Skip to content

Commit 0fe8f98

Browse files
Fix review
1 parent d673f1b commit 0fe8f98

File tree

2 files changed

+60
-71
lines changed

2 files changed

+60
-71
lines changed

src/ir/effects.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -297,10 +297,9 @@ class EffectAnalyzer {
297297
}
298298

299299
// Check if this has any ordering-relevant effects beyond local variable
300-
// access. This is used by SimplifyLocals' reverse-index optimization to
301-
// classify sinkables: those with only local effects can be looked up via
302-
// the local reverse index, while those with non-local effects need broader
303-
// conflict checks.
300+
// access. When this returns false, the only possible conflicts with other
301+
// code are through local variable reads/writes, which can be checked via
302+
// targeted lookups rather than comparing against all other expressions.
304303
//
305304
// This is derived from the same helpers used by orderedBefore(), so it
306305
// automatically stays in sync when new effect types are added.

src/passes/SimplifyLocals.cpp

Lines changed: 57 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -93,29 +93,22 @@ struct SimplifyLocals
9393

9494
// Reverse index: for each local L, tracks which sinkable keys have effects
9595
// that read L. This allows checkInvalidations to find conflicting sinkables
96-
// in O(|current effects|) instead of O(|all sinkables|).
97-
std::unordered_map<Index, std::unordered_set<Index>> localReadBySinkable_;
96+
// in O(|current effects|) instead of O(|all sinkables|) when the current
97+
// expression only has local effects.
98+
std::unordered_map<Index, std::unordered_set<Index>> localReadBySinkable;
9899

99100
// Reverse index: for each local L, tracks which sinkable keys have effects
100101
// that write L. A sinkable at key K always writes K, but may also write
101102
// other locals if its value contains nested local.sets.
102-
std::unordered_map<Index, std::unordered_set<Index>> localWrittenBySinkable_;
103-
104-
// Sinkable keys that have non-local ordering-relevant effects (calls,
105-
// memory, control flow, etc.). These need full orderedAfter checks when
106-
// the current expression also has non-local effects.
107-
std::unordered_set<Index> heavySinkables_;
103+
std::unordered_map<Index, std::unordered_set<Index>> localWrittenBySinkable;
108104

109105
void registerSinkable(Index key) {
110106
auto& effects = sinkables.at(key).effects;
111107
for (auto L : effects.localsRead) {
112-
localReadBySinkable_[L].insert(key);
108+
localReadBySinkable[L].insert(key);
113109
}
114110
for (auto L : effects.localsWritten) {
115-
localWrittenBySinkable_[L].insert(key);
116-
}
117-
if (effects.hasNonLocalOrderingEffects()) {
118-
heavySinkables_.insert(key);
111+
localWrittenBySinkable[L].insert(key);
119112
}
120113
}
121114

@@ -126,37 +119,34 @@ struct SimplifyLocals
126119
}
127120
auto& effects = it->second.effects;
128121
for (auto L : effects.localsRead) {
129-
auto mapIt = localReadBySinkable_.find(L);
130-
if (mapIt != localReadBySinkable_.end()) {
122+
auto mapIt = localReadBySinkable.find(L);
123+
if (mapIt != localReadBySinkable.end()) {
131124
mapIt->second.erase(key);
132125
if (mapIt->second.empty()) {
133-
localReadBySinkable_.erase(mapIt);
126+
localReadBySinkable.erase(mapIt);
134127
}
135128
}
136129
}
137130
for (auto L : effects.localsWritten) {
138-
auto mapIt = localWrittenBySinkable_.find(L);
139-
if (mapIt != localWrittenBySinkable_.end()) {
131+
auto mapIt = localWrittenBySinkable.find(L);
132+
if (mapIt != localWrittenBySinkable.end()) {
140133
mapIt->second.erase(key);
141134
if (mapIt->second.empty()) {
142-
localWrittenBySinkable_.erase(mapIt);
135+
localWrittenBySinkable.erase(mapIt);
143136
}
144137
}
145138
}
146-
heavySinkables_.erase(key);
147139
}
148140

149141
void clearSinkables() {
150142
sinkables.clear();
151-
localReadBySinkable_.clear();
152-
localWrittenBySinkable_.clear();
153-
heavySinkables_.clear();
143+
localReadBySinkable.clear();
144+
localWrittenBySinkable.clear();
154145
}
155146

156147
Sinkables takeSinkables() {
157-
localReadBySinkable_.clear();
158-
localWrittenBySinkable_.clear();
159-
heavySinkables_.clear();
148+
localReadBySinkable.clear();
149+
localWrittenBySinkable.clear();
160150
return std::move(sinkables);
161151
}
162152

@@ -384,56 +374,56 @@ struct SimplifyLocals
384374
}
385375

386376
void checkInvalidations(EffectAnalyzer& effects) {
387-
// Use targeted lookups instead of iterating all sinkables.
388-
// We collect candidate sinkable keys that *might* conflict, then verify.
389-
std::unordered_set<Index> candidates;
390-
391-
// Local conflicts via reverse indices.
392-
// When the current expression reads local L, any sinkable that writes L
393-
// has a write-read conflict.
394-
for (auto L : effects.localsRead) {
395-
auto it = localWrittenBySinkable_.find(L);
396-
if (it != localWrittenBySinkable_.end()) {
397-
candidates.insert(it->second.begin(), it->second.end());
377+
// If the current expression only has local effects, we can use reverse
378+
// indices to find exactly the conflicting sinkables in O(|locals|) instead
379+
// of iterating all sinkables. This is the common case for local.get,
380+
// local.set, const, and simple arithmetic expressions.
381+
if (!effects.hasNonLocalOrderingEffects()) {
382+
std::unordered_set<Index> candidates;
383+
// When the current expression reads local L, any sinkable that writes L
384+
// has a write-read conflict.
385+
for (auto L : effects.localsRead) {
386+
auto it = localWrittenBySinkable.find(L);
387+
if (it != localWrittenBySinkable.end()) {
388+
candidates.insert(it->second.begin(), it->second.end());
389+
}
398390
}
399-
}
400-
// When the current expression writes local L, any sinkable that reads L
401-
// (read-write conflict) or writes L (write-write conflict) is a candidate.
402-
for (auto L : effects.localsWritten) {
403-
auto it = localReadBySinkable_.find(L);
404-
if (it != localReadBySinkable_.end()) {
405-
candidates.insert(it->second.begin(), it->second.end());
391+
// When the current expression writes local L, any sinkable that reads L
392+
// (read-write conflict) or writes L (write-write conflict) is a
393+
// candidate.
394+
for (auto L : effects.localsWritten) {
395+
auto it = localReadBySinkable.find(L);
396+
if (it != localReadBySinkable.end()) {
397+
candidates.insert(it->second.begin(), it->second.end());
398+
}
399+
auto it2 = localWrittenBySinkable.find(L);
400+
if (it2 != localWrittenBySinkable.end()) {
401+
candidates.insert(it2->second.begin(), it2->second.end());
402+
}
406403
}
407-
auto it2 = localWrittenBySinkable_.find(L);
408-
if (it2 != localWrittenBySinkable_.end()) {
409-
candidates.insert(it2->second.begin(), it2->second.end());
404+
std::vector<Index> invalidated;
405+
for (auto key : candidates) {
406+
auto it = sinkables.find(key);
407+
if (it != sinkables.end() && effects.orderedAfter(it->second.effects)) {
408+
invalidated.push_back(key);
409+
}
410410
}
411-
}
412-
413-
// Non-local conflicts: if the current expression has non-local effects,
414-
// check sinkables that also have non-local effects.
415-
if (effects.hasNonLocalOrderingEffects()) {
416-
candidates.insert(heavySinkables_.begin(), heavySinkables_.end());
417-
}
418-
// If current transfers control flow, all sinkables with any side effects
419-
// (including local access) are invalidated. Since all sinkables access
420-
// locals, this means all of them.
421-
if (effects.transfersControlFlow()) {
422-
for (auto& [key, _] : sinkables) {
423-
candidates.insert(key);
411+
for (auto key : invalidated) {
412+
eraseSinkable(key);
424413
}
414+
return;
425415
}
426416

427-
// Verify candidates with the full ordering check and invalidate.
417+
// The current expression has non-local effects (memory, calls, control
418+
// flow, etc.), so we must check all sinkables.
428419
std::vector<Index> invalidated;
429-
for (auto key : candidates) {
430-
auto it = sinkables.find(key);
431-
if (it != sinkables.end() && effects.orderedAfter(it->second.effects)) {
432-
invalidated.push_back(key);
420+
for (auto& [index, info] : sinkables) {
421+
if (effects.orderedAfter(info.effects)) {
422+
invalidated.push_back(index);
433423
}
434424
}
435-
for (auto key : invalidated) {
436-
eraseSinkable(key);
425+
for (auto index : invalidated) {
426+
eraseSinkable(index);
437427
}
438428
}
439429

0 commit comments

Comments
 (0)