Skip to content

Commit 924c32b

Browse files
authored
[NFC] Store function effects on functions (#7302)
This fixes an unsettling bug where, with a long-enough sequence of passes, we might create a function A, compute it has no effects, remove that function, create another function with the same but with effects, and think the old effects apply to it. This is basically a danger of using a map from function name to effects. To avoid that, store the effects on function objects themselves.
1 parent 2e26ec6 commit 924c32b

5 files changed

Lines changed: 29 additions & 38 deletions

File tree

src/ir/effects.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ class EffectAnalyzer {
2929
public:
3030
EffectAnalyzer(const PassOptions& passOptions, Module& module)
3131
: ignoreImplicitTraps(passOptions.ignoreImplicitTraps),
32-
trapsNeverHappen(passOptions.trapsNeverHappen),
33-
funcEffectsMap(passOptions.funcEffectsMap), module(module),
32+
trapsNeverHappen(passOptions.trapsNeverHappen), module(module),
3433
features(module.features) {}
3534

3635
EffectAnalyzer(const PassOptions& passOptions,
@@ -47,7 +46,6 @@ class EffectAnalyzer {
4746

4847
bool ignoreImplicitTraps;
4948
bool trapsNeverHappen;
50-
std::shared_ptr<FuncEffectsMap> funcEffectsMap;
5149
Module& module;
5250
FeatureSet features;
5351

@@ -523,12 +521,14 @@ class EffectAnalyzer {
523521
return;
524522
}
525523

524+
// Get the target's effects, if they exist. Note that we must handle the
525+
// case of the function not yet existing (we may be executed in the middle
526+
// of a pass, which may have built up calls but not the targets of those
527+
// calls; in such a case, we do not find the targets and therefore assume
528+
// we know nothing about the effects, which is safe).
526529
const EffectAnalyzer* targetEffects = nullptr;
527-
if (parent.funcEffectsMap) {
528-
auto iter = parent.funcEffectsMap->find(curr->target);
529-
if (iter != parent.funcEffectsMap->end()) {
530-
targetEffects = &iter->second;
531-
}
530+
if (auto* target = parent.module.getFunctionOrNull(curr->target)) {
531+
targetEffects = target->effects.get();
532532
}
533533

534534
if (curr->isReturn) {

src/pass.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,6 @@ struct InliningOptions {
9999
Index partialInliningIfs = 0;
100100
};
101101

102-
// Forward declaration for FuncEffectsMap.
103-
class EffectAnalyzer;
104-
105-
using FuncEffectsMap = std::unordered_map<Name, EffectAnalyzer>;
106-
107102
struct PassOptions {
108103
friend Pass;
109104

@@ -237,15 +232,6 @@ struct PassOptions {
237232
// Passes to skip and not run.
238233
std::unordered_set<std::string> passesToSkip;
239234

240-
// Effect info computed for functions. One pass can generate this and then
241-
// other passes later can benefit from it. It is up to the sequence of passes
242-
// to update or discard this when necessary - in particular, when new effects
243-
// are added to a function this must be changed or we may optimize
244-
// incorrectly. However, it is extremely rare for a pass to *add* effects;
245-
// passes normally only remove effects. Passes that do add effects must set
246-
// addsEffects() so the pass runner is aware of them.
247-
std::shared_ptr<FuncEffectsMap> funcEffectsMap;
248-
249235
// -Os is our default
250236
static constexpr const int DEFAULT_OPTIMIZE_LEVEL = 2;
251237
static constexpr const int DEFAULT_SHRINK_LEVEL = 1;

src/passes/GlobalEffects.cpp

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -181,30 +181,25 @@ struct GenerateGlobalEffects : public Pass {
181181
}
182182
}
183183

184-
// Generate the final data structure, starting from a blank slate where
185-
// nothing is known.
186-
auto& funcEffectsMap = getPassOptions().funcEffectsMap;
187-
funcEffectsMap.reset();
188-
184+
// Generate the final data, starting from a blank slate where nothing is
185+
// known.
189186
for (auto& [func, info] : analysis.map) {
187+
func->effects.reset();
190188
if (!info.effects) {
191-
// Add no entry to funcEffectsMap, since nothing is known.
192189
continue;
193190
}
194191

195-
// Only allocate a new funcEffectsMap here when we actually have data for
196-
// it (which might make later effect computation slightly faster, to
197-
// quickly skip the funcEffectsMap code path).
198-
if (!funcEffectsMap) {
199-
funcEffectsMap = std::make_shared<FuncEffectsMap>();
200-
}
201-
funcEffectsMap->emplace(func->name, *info.effects);
192+
func->effects = std::make_shared<EffectAnalyzer>(*info.effects);
202193
}
203194
}
204195
};
205196

206197
struct DiscardGlobalEffects : public Pass {
207-
void run(Module* module) override { getPassOptions().funcEffectsMap.reset(); }
198+
void run(Module* module) override {
199+
for (auto& func : module->functions) {
200+
func->effects.reset();
201+
}
202+
}
208203
};
209204

210205
Pass* createGenerateGlobalEffectsPass() { return new GenerateGlobalEffects(); }

src/passes/pass.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,9 +1044,9 @@ void PassRunner::handleAfterEffects(Pass* pass, Function* func) {
10441044
TypeUpdating::handleNonDefaultableLocals(func, *wasm);
10451045
}
10461046

1047-
if (options.funcEffectsMap && pass->addsEffects()) {
1047+
if (pass->addsEffects()) {
10481048
// Effects were added, so discard any computed effects for this function.
1049-
options.funcEffectsMap->erase(func->name);
1049+
func->effects.reset();
10501050
}
10511051
}
10521052

src/wasm.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2137,6 +2137,9 @@ struct BinaryLocations {
21372137
std::unordered_map<Function*, FunctionLocations> functions;
21382138
};
21392139

2140+
// Forward declaration for FuncEffectsMap.
2141+
class EffectAnalyzer;
2142+
21402143
class Function : public Importable {
21412144
public:
21422145
HeapType type = HeapType(Signature()); // parameters and return value
@@ -2183,6 +2186,13 @@ class Function : public Importable {
21832186
delimiterLocations;
21842187
BinaryLocations::FunctionLocations funcLocation;
21852188

2189+
// The effects for this function, if they have been computed. We use a shared
2190+
// ptr here to avoid compilation errors with the forward-declared
2191+
// EffectAnalyzer.
2192+
//
2193+
// See addsEffects() in pass.h for more details.
2194+
std::shared_ptr<EffectAnalyzer> effects;
2195+
21862196
// Inlining metadata: whether to disallow full and/or partial inlining (for
21872197
// details on what those mean, see Inlining.cpp).
21882198
bool noFullInline = false;

0 commit comments

Comments
 (0)