diff --git a/src/ir/LocalGraph.cpp b/src/ir/LocalGraph.cpp index af13707314e..a5495b5b33f 100644 --- a/src/ir/LocalGraph.cpp +++ b/src/ir/LocalGraph.cpp @@ -281,6 +281,8 @@ struct LocalGraphFlower }); if (lastSet != pred->lastSets.end()) { // There is a set here, apply it, and stop the flow. + // TODO: If we find a computed get, apply its sets and stop? That + // could help but it requires more info on FlowBlock. for (auto* get : gets) { getSetsMap[get].insert(lastSet->second); } @@ -512,7 +514,9 @@ void LocalGraph::computeSetInfluences() { } } -void LocalGraph::computeGetInfluences() { +static void +doComputeGetInfluences(const LocalGraphBase::Locations& locations, + LocalGraphBase::GetInfluencesMap& getInfluences) { for (auto& [curr, _] : locations) { if (auto* set = curr->dynCast()) { FindAll findAll(set->value); @@ -523,6 +527,10 @@ void LocalGraph::computeGetInfluences() { } } +void LocalGraph::computeGetInfluences() { + doComputeGetInfluences(locations, getInfluences); +} + void LocalGraph::computeSSAIndexes() { std::unordered_map> indexSets; for (auto& [get, sets] : getSetsMap) { @@ -555,13 +563,12 @@ LazyLocalGraph::LazyLocalGraph(Function* func, Module* module) : LocalGraphBase(func, module) {} void LazyLocalGraph::makeFlower() const { - // Lazy graphs do not provide |locations| publicly. TODO: perhaps refactor to - // avoid filling in this dummy data structure, but we may want to add a lazy - // version of it too, so see which makes sense first. - LocalGraph::Locations locations; + // |locations| is set here and filled in by |flower|. + assert(!locations); + locations.emplace(); flower = - std::make_unique(getSetsMap, locations, func, module); + std::make_unique(getSetsMap, *locations, func, module); flower->prepareLaziness(); @@ -585,6 +592,9 @@ LazyLocalGraph::~LazyLocalGraph() { } void LazyLocalGraph::computeGetSets(LocalGet* get) const { + // We must never repeat work. + assert(!getSetsMap.count(get)); + if (!flower) { makeFlower(); } @@ -592,10 +602,40 @@ void LazyLocalGraph::computeGetSets(LocalGet* get) const { } void LazyLocalGraph::computeSetInfluences(LocalSet* set) const { + // We must never repeat work. + assert(!setInfluences.count(set)); + if (!flower) { makeFlower(); } flower->computeSetInfluences(set, setInfluences); } +void LazyLocalGraph::computeGetInfluences() const { + // We must never repeat work. + assert(!getInfluences); + + // We do not need any flow for this, but we do need |locations| to be filled + // in. + getLocations(); + assert(locations); + + getInfluences.emplace(); + doComputeGetInfluences(*locations, *getInfluences); +} + +void LazyLocalGraph::computeLocations() const { + // We must never repeat work. + assert(!locations); + + // |flower| fills in |locations| as it scans the function. + // + // In theory we could be even lazier here, but it is nice that flower will + // fill in the locations as it goes, avoiding an additional pass. And, in + // practice, if we ask for locations then we likely need other things anyhow. + if (!flower) { + makeFlower(); + } +} + } // namespace wasm diff --git a/src/ir/local-graph.h b/src/ir/local-graph.h index 46bc135e0bd..6f1e621cfdf 100644 --- a/src/ir/local-graph.h +++ b/src/ir/local-graph.h @@ -202,17 +202,43 @@ struct LazyLocalGraph : public LocalGraphBase { } return iter->second; } + const GetInfluences& getGetInfluences(LocalGet* get) const { + if (!getInfluences) { + computeGetInfluences(); + assert(getInfluences); + } + return (*getInfluences)[get]; + } + + const Locations& getLocations() const { + if (!locations) { + computeLocations(); + assert(locations); + } + return *locations; + } private: // These data structures are mutable so that we can memoize. mutable GetSetsMap getSetsMap; mutable SetInfluencesMap setInfluences; + // The entire |getInfluences| is computed once the first request for one + // arrives, so the entire thing is either present or not, unlike setInfluences + // which is fine-grained. The difference is that the influences of a get may + // include sets of other indexes, so there is no simple way to lazify that + // computation. + mutable std::optional getInfluences; + mutable std::optional locations; // Compute the sets for a get and store them on getSetsMap. void computeGetSets(LocalGet* get) const; // Compute influences for a set and store them on setInfluences. void computeSetInfluences(LocalSet* set) const; + // Compute influences for all gets and store them on getInfluences. + void computeGetInfluences() const; + // Compute locations and store them on getInfluences. + void computeLocations() const; // This remains alive as long as we are, so that we can compute things lazily. // It is mutable as when we construct this is an internal detail, that does diff --git a/src/passes/Precompute.cpp b/src/passes/Precompute.cpp index 0b8ead056a8..709fd7d3d85 100644 --- a/src/passes/Precompute.cpp +++ b/src/passes/Precompute.cpp @@ -749,9 +749,9 @@ struct Precompute // Using the graph of get-set interactions, do a constant-propagation type // operation: note which sets are assigned locals, then see if that lets us // compute other sets as locals (since some of the gets they read may be - // constant). First, compute all influences/dependencies. - LocalGraph localGraph(func, getModule()); - localGraph.computeInfluences(); + // constant). We do this lazily as most locals do not end up with constant + // values that we can propagate. + LazyLocalGraph localGraph(func, getModule()); // A map of sets to their constant values. If a set does not appear here // then it is not constant, like |getValues|. @@ -875,7 +875,7 @@ struct Precompute // Check all gets and sets to find which are constant, mark them as such, // and add propagation work based on that. - for (auto& [curr, _] : localGraph.locations) { + for (auto& [curr, _] : localGraph.getLocations()) { if (auto* set = curr->dynCast()) { checkConstantSet(set); } else {