diff options
Diffstat (limited to 'src/ir/LocalGraph.cpp')
-rw-r--r-- | src/ir/LocalGraph.cpp | 206 |
1 files changed, 166 insertions, 40 deletions
diff --git a/src/ir/LocalGraph.cpp b/src/ir/LocalGraph.cpp index 7e877e65e..33055ad49 100644 --- a/src/ir/LocalGraph.cpp +++ b/src/ir/LocalGraph.cpp @@ -16,10 +16,11 @@ #include <iterator> -#include <cfg/cfg-traversal.h> -#include <ir/find_all.h> -#include <ir/local-graph.h> -#include <wasm-builder.h> +#include "cfg/cfg-traversal.h" +#include "ir/find_all.h" +#include "ir/local-graph.h" +#include "support/unique_deferring_queue.h" +#include "wasm-builder.h" namespace wasm { @@ -42,21 +43,27 @@ struct Info { // flow helper class. flows the gets to their sets struct LocalGraphFlower - : public CFGWalker<LocalGraphFlower, Visitor<LocalGraphFlower>, Info> { + : public CFGWalker<LocalGraphFlower, + UnifiedExpressionVisitor<LocalGraphFlower>, + Info> { LocalGraph::GetSetsMap& getSetsMap; LocalGraph::Locations& locations; Function* func; + std::optional<Expression::Id> queryClass; LocalGraphFlower(LocalGraph::GetSetsMap& getSetsMap, LocalGraph::Locations& locations, Function* func, - Module* module) - : getSetsMap(getSetsMap), locations(locations), func(func) { + Module* module, + std::optional<Expression::Id> queryClass = std::nullopt) + : getSetsMap(getSetsMap), locations(locations), func(func), + queryClass(queryClass) { setFunction(func); setModule(module); // create the CFG by walking the IR - CFGWalker<LocalGraphFlower, Visitor<LocalGraphFlower>, Info>:: - doWalkFunction(func); + CFGWalker<LocalGraphFlower, + UnifiedExpressionVisitor<LocalGraphFlower>, + Info>::doWalkFunction(func); } BasicBlock* makeBasicBlock() { return new BasicBlock(); } @@ -67,25 +74,22 @@ struct LocalGraphFlower // cfg traversal work - static void doVisitLocalGet(LocalGraphFlower* self, Expression** currp) { - auto* curr = (*currp)->cast<LocalGet>(); - // if in unreachable code, skip - if (!self->currBasicBlock) { + void visitExpression(Expression* curr) { + // If in unreachable code, skip. + if (!currBasicBlock) { return; } - self->currBasicBlock->contents.actions.emplace_back(curr); - self->locations[curr] = currp; - } - static void doVisitLocalSet(LocalGraphFlower* self, Expression** currp) { - auto* curr = (*currp)->cast<LocalSet>(); - // if in unreachable code, skip - if (!self->currBasicBlock) { - return; + // If this is a relevant action (a get or set, or there is a query class + // and this is an instance of it) then note it. + if (curr->is<LocalGet>() || curr->is<LocalSet>() || + (queryClass && curr->_id == *queryClass)) { + currBasicBlock->contents.actions.emplace_back(curr); + locations[curr] = getCurrentPointer(); + if (auto* set = curr->dynCast<LocalSet>()) { + currBasicBlock->contents.lastSets[set->index] = set; + } } - self->currBasicBlock->contents.actions.emplace_back(curr); - self->currBasicBlock->contents.lastSets[curr->index] = curr; - self->locations[curr] = currp; } // Each time we flow a get (or set of gets) to find its sets, we mark a @@ -203,9 +207,8 @@ struct LocalGraphFlower auto* action = actions[i]; if (auto* get = action->dynCast<LocalGet>()) { allGets[get->index].push_back(get); - } else { + } else if (auto* set = action->dynCast<LocalSet>()) { // This set is the only set for all those gets. - auto* set = action->cast<LocalSet>(); auto& gets = allGets[set->index]; for (auto* get : gets) { getSetsMap[get].insert(set); @@ -302,12 +305,8 @@ struct LocalGraphFlower // initially, but instead fill in these data structures that let us do so // later for individual gets. Specifically we need to find the location of a // local.get in the CFG. - struct BlockLocation { - // The basic block an item is in. - FlowBlock* block = nullptr; - // The index in that block that the item is at. - Index index; - }; + using BlockLocation = std::pair<FlowBlock*, Index>; + std::unordered_map<LocalGet*, BlockLocation> getLocations; // In lazy mode we also need to categorize gets and sets by their index. @@ -331,8 +330,6 @@ struct LocalGraphFlower getsByIndex[get->index].push_back(get); } else if (auto* set = actions[i]->dynCast<LocalSet>()) { setsByIndex[set->index].push_back(set); - } else { - WASM_UNREACHABLE("bad action"); } } } @@ -391,9 +388,8 @@ struct LocalGraphFlower // It will have the same sets as us. gets.push_back(otherGet); } - } else { + } else if (auto* set = curr->dynCast<LocalSet>()) { // This is a set. - auto* set = curr->cast<LocalSet>(); if (set->index == index) { // This is the only set writing to our gets. for (auto* get : gets) { @@ -444,6 +440,86 @@ struct LocalGraphFlower } } } + + // Given a bunch of gets, see if any of them are reached by the given set + // despite the obstacle expression stopping the flow whenever it is reached. + // That is, the obstacle is considered as if it was a set of the same index, + // which would trample the value and stop the set from influencing it. + LocalGraphBase::SetInfluences + getSetInfluencesGivenObstacle(LocalSet* set, + const LocalGraphBase::SetInfluences& gets, + Expression* obstacle) { + LocalGraphBase::SetInfluences ret; + // Normally flowing backwards is faster, as we start from actual gets (and + // so we avoid flowing past all the gets to large swaths of the program that + // we don't care about; and in reverse, we might go all the way to the + // entry in a wasteful manner, but most gets have an actual set, and do not + // read the default value). The situation here is a bit different, though, + // in that we might expect that going forward from the set would quickly + // reach the obstacle and stop. Still, a single branch away would cause us + // to scan lots of blocks potentially, and might not be that rare in + // general, so go backwards. (Many uninteresting branches away, that reach + // no relevant gets, are common when exceptions are enabled, as every call + // gets a branch.) + for (auto* get : gets) { + auto [block, index] = getLocations[get]; + if (!block) { + // We did not find location info for this get, which means it is + // unreachable. + continue; + } + + // Use a work queue of block locations to scan backwards from. + // Specifically we must scan the first index above it (i.e., the original + // location has a local.get there, so we start one before it). + UniqueNonrepeatingDeferredQueue<BlockLocation> work; + work.push(BlockLocation{block, index}); + auto foundSet = false; + // Flow while there is stuff to flow, and while we haven't found the set + // (once we find it, we add the get and can move on to the next get). + while (!work.empty() && !foundSet) { + auto [block, index] = work.pop(); + + // Scan backwards through this block. + while (1) { + // If we finished scanning this block (we reached the top), flow to + // predecessors. + if (index == 0) { + for (auto* pred : block->in) { + // We will scan pred from its very end. + work.push(BlockLocation{pred, Index(pred->actions.size())}); + } + break; + } + + // Continue onwards. + index--; + auto* action = block->actions[index]; + if (auto* otherSet = action->dynCast<LocalSet>()) { + if (otherSet == set) { + // We arrived at the set: add this get and stop flowing it. + ret.insert(get); + foundSet = true; + break; + } + if (otherSet->index == set->index) { + // This is another set of the same index, which halts the flow. + break; + } + } else if (action == obstacle) { + // We ran into the obstacle. Halt this flow. + break; + } + // TODO: If the action is one of the gets we are scanning, then + // either we have processed it already, or will do so later, and we + // can halt. As an optimization, we could check if we've processed + // it already and act accordingly. + } + } + } + + return ret; + } }; // LocalGraph implementation @@ -559,16 +635,18 @@ bool LocalGraph::isSSA(Index x) { return SSAIndexes.count(x); } // LazyLocalGraph -LazyLocalGraph::LazyLocalGraph(Function* func, Module* module) - : LocalGraphBase(func, module) {} +LazyLocalGraph::LazyLocalGraph(Function* func, + Module* module, + std::optional<Expression::Id> queryClass) + : LocalGraphBase(func, module), queryClass(queryClass) {} void LazyLocalGraph::makeFlower() const { // |locations| is set here and filled in by |flower|. assert(!locations); locations.emplace(); - flower = - std::make_unique<LocalGraphFlower>(getSetsMap, *locations, func, module); + flower = std::make_unique<LocalGraphFlower>( + getSetsMap, *locations, func, module, queryClass); flower->prepareLaziness(); @@ -670,4 +748,52 @@ void LazyLocalGraph::computeLocations() const { } } +LocalGraphBase::SetInfluences LazyLocalGraph::canMoveSet(LocalSet* set, + Expression* to) { + // We must have been initialized with the proper query class, so that we + // prepared the flower (if it was computed before) with that class in the + // graph. + assert(queryClass && to->_id == *queryClass); + + if (!flower) { + makeFlower(); + } + + // To compute this property, we'll do a flow from the gets that the set + // originally reaches. No other get is relevant. + auto originalGets = getSetInfluences(set); + + // To see which gets pose a problem, see which gets are still influenced by + // the set, if we consider |to| to be another set of that index, that is, an + // obstacle on the way, that tramples that local index's value. Any such + // influenced get is a problem, for example: + // + // 1. set + // 2. get + // 3. call + // 4. get + // + // The set can still influence the get on line 2, if we consider the call to + // be an obstacle. Looking at it another way, any get that is no longer + // influenced, given the obstacle, is a get that is only influenced by the + // obstacle itself, meaning that moving the set to the obstacle is valid. This + // is a slight simplification, though, since other sets may be involved: + // + // if (..) { + // x = ..; + // a(x) + // b(); + // c(x); + // } + // d(x); + // + // Say we consider moving the set of x to b(). a(x) uses x in a manner that + // will notice that, but not c(x) or d(x). c(x) is dominated by the set, but + // d(x) is not. That is, moving the set to b() leaves the set's influence + // unchanged on c(x), where that influence is full, and also on d(x), where it + // is only partial (shared with whatever value is present in x before the if). + // (But moving the set to b() does alter the set's influence on a(x)). + return flower->getSetInfluencesGivenObstacle(set, originalGets, to); +} + } // namespace wasm |