Assign to shared_ptr directly The Effects object is now shared within each SCC
diff --git a/src/passes/GlobalEffects.cpp b/src/passes/GlobalEffects.cpp index bdb62b1..c784cc5 100644 --- a/src/passes/GlobalEffects.cpp +++ b/src/passes/GlobalEffects.cpp
@@ -29,14 +29,7 @@ namespace { -constexpr auto UnknownEffects = std::nullopt; - struct FuncInfo { - // Effects in this function. nullopt / UnknownEffects means that we don't know - // what effects this function has, so we conservatively assume all effects. - // Nullopt cases won't be copied to Function::effects. - std::optional<EffectAnalyzer> effects; - // Directly-called functions from this function. std::unordered_set<Name> calledFunctions; }; @@ -46,25 +39,25 @@ ModuleUtils::ParallelFunctionAnalysis<FuncInfo> analysis( module, [&](Function* func, FuncInfo& funcInfo) { if (func->imported()) { - // Imports can do anything, so we need to assume the worst anyhow, - // which is the same as not specifying any effects for them in the - // map (which we do by not setting funcInfo.effects). + // Imports can do anything, so we need to assume the worst anyhow. + func->effects = nullptr; return; } // Gather the effects. - funcInfo.effects.emplace(passOptions, module, func); + func->effects = + std::make_shared<EffectAnalyzer>(passOptions, module, func); - if (funcInfo.effects->calls) { + if (func->effects->calls) { // There are calls in this function, which we will analyze in detail. // Clear the |calls| field first, and we'll handle calls of all sorts // below. - funcInfo.effects->calls = false; + func->effects->calls = false; // Clear throws as well, as we are "forgetting" calls right now, and // want to forget their throwing effect as well. If we see something // else that throws, below, then we'll note that there. - funcInfo.effects->throws_ = false; + func->effects->throws_ = false; struct CallScanner : public PostWalker<CallScanner, @@ -72,11 +65,13 @@ Module& wasm; const PassOptions& options; FuncInfo& funcInfo; + Function* func; CallScanner(Module& wasm, const PassOptions& options, - FuncInfo& funcInfo) - : wasm(wasm), options(options), funcInfo(funcInfo) {} + FuncInfo& funcInfo, + Function* func) + : wasm(wasm), options(options), funcInfo(funcInfo), func(func) {} void visitExpression(Expression* curr) { ShallowEffectAnalyzer effects(options, wasm, curr); @@ -88,18 +83,18 @@ // worst. To do so, clear the effects, which indicates nothing // is known (so anything is possible). // TODO: We could group effects by function type etc. - funcInfo.effects = UnknownEffects; + func->effects = nullptr; } else { // No call here, but update throwing if we see it. (Only do so, // however, if we have effects; if we cleared it - see before - // then we assume the worst anyhow, and have nothing to update.) - if (effects.throws_ && funcInfo.effects) { - funcInfo.effects->throws_ = true; + if (effects.throws_ && func->effects) { + func->effects->throws_ = true; } } } }; - CallScanner scanner(module, passOptions, funcInfo); + CallScanner scanner(module, passOptions, funcInfo, func); scanner.walkFunction(func); } }); @@ -171,15 +166,15 @@ CallGraphSCCs sccs(allFuncs, funcInfos, callGraph, module); std::unordered_map<Function*, int> sccMembers; - std::unordered_map<int, std::optional<EffectAnalyzer>> componentEffects; + std::unordered_map<int, std::shared_ptr<EffectAnalyzer>> componentEffects; int ccIndex = 0; for (auto ccIterator : sccs) { ccIndex++; - std::optional<EffectAnalyzer>& ccEffects = componentEffects[ccIndex]; + std::shared_ptr<EffectAnalyzer>& ccEffects = componentEffects[ccIndex]; std::vector<Function*> ccFuncs(ccIterator.begin(), ccIterator.end()); - ccEffects.emplace(passOptions, module); + ccEffects = std::make_shared<EffectAnalyzer>(passOptions, module); for (Function* f : ccFuncs) { sccMembers.emplace(f, ccIndex); @@ -199,25 +194,25 @@ // Merge in effects from callees for (int calleeScc : calleeSccs) { const auto& calleeComponentEffects = componentEffects.at(calleeScc); - if (calleeComponentEffects == UnknownEffects) { - ccEffects = UnknownEffects; + if (calleeComponentEffects == nullptr) { + ccEffects.reset(); break; } - else if (ccEffects != UnknownEffects) { + else if (ccEffects != nullptr) { ccEffects->mergeIn(*calleeComponentEffects); } } // Add trap effects for potential cycles. if (ccFuncs.size() > 1) { - if (ccEffects != UnknownEffects) { + if (ccEffects != nullptr) { ccEffects->trap = true; } } else { auto* func = ccFuncs[0]; if (funcInfos.at(func).calledFunctions.contains(func->name)) { - if (ccEffects != UnknownEffects) { + if (ccEffects != nullptr) { ccEffects->trap = true; } } @@ -226,9 +221,9 @@ // Aggregate effects within this CC if (ccEffects) { for (Function* f : ccFuncs) { - const auto& effects = funcInfos.at(f).effects; - if (effects == UnknownEffects) { - ccEffects = UnknownEffects; + const auto& effects = f->effects; + if (effects == nullptr) { + ccEffects.reset(); break; } @@ -238,26 +233,11 @@ // Assign each function's effects to its CC effects. for (Function* f : ccFuncs) { - if (!ccEffects) { - funcInfos.at(f).effects = UnknownEffects; - } else { - funcInfos.at(f).effects.emplace(*ccEffects); - } + f->effects = ccEffects; } } } -void copyEffectsToFunctions(const std::map<Function*, FuncInfo> funcInfos) { - for (auto& [func, info] : funcInfos) { - func->effects.reset(); - if (!info.effects) { - continue; - } - - func->effects = std::make_shared<EffectAnalyzer>(*info.effects); - } -} - struct GenerateGlobalEffects : public Pass { void run(Module* module) override { std::map<Function*, FuncInfo> funcInfos = @@ -266,8 +246,6 @@ auto callGraph = buildCallGraph(*module, funcInfos); propagateEffects(*module, getPassOptions(), funcInfos, callGraph); - - copyEffectsToFunctions(funcInfos); } };
diff --git a/test/lit/passes/global-effects.wast b/test/lit/passes/global-effects.wast index 1125f73..d7e9367 100644 --- a/test/lit/passes/global-effects.wast +++ b/test/lit/passes/global-effects.wast
@@ -92,7 +92,7 @@ ;; WITHOUT-NEXT: (call $cycle-2) ;; WITHOUT-NEXT: ) ;; INCLUDE: (func $cycle-1 (type $void) - ;; INCLUDE-NEXT: (call $cycle-2) + ;; INCLUDE-NEXT: (nop) ;; INCLUDE-NEXT: ) (func $cycle-1 ;; $cycle-1 and -2 form a cycle together, in which no call can be removed. @@ -103,7 +103,7 @@ ;; WITHOUT-NEXT: (call $cycle-1) ;; WITHOUT-NEXT: ) ;; INCLUDE: (func $cycle-2 (type $void) - ;; INCLUDE-NEXT: (call $cycle-1) + ;; INCLUDE-NEXT: (nop) ;; INCLUDE-NEXT: ) (func $cycle-2 (call $cycle-1)