diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cfg/cfg-traversal.h | 2 | ||||
-rw-r--r-- | src/cfg/liveness-traversal.h | 25 | ||||
-rw-r--r-- | src/ir/LocalGraph.cpp | 375 | ||||
-rw-r--r-- | src/ir/local-graph.h | 66 | ||||
-rw-r--r-- | src/passes/CoalesceLocals.cpp | 2 | ||||
-rw-r--r-- | src/passes/MergeLocals.cpp | 4 | ||||
-rw-r--r-- | src/passes/Precompute.cpp | 6 | ||||
-rw-r--r-- | src/passes/SSAify.cpp | 2 |
8 files changed, 188 insertions, 294 deletions
diff --git a/src/cfg/cfg-traversal.h b/src/cfg/cfg-traversal.h index 13bb0c744..73463eaa3 100644 --- a/src/cfg/cfg-traversal.h +++ b/src/cfg/cfg-traversal.h @@ -66,7 +66,7 @@ struct CFGWalker : public ControlFlowWalker<SubType, VisitorType> { std::vector<BasicBlock*> loopStack; void startBasicBlock() { - currBasicBlock = makeBasicBlock(); + currBasicBlock = ((SubType*)this)->makeBasicBlock(); basicBlocks.push_back(std::unique_ptr<BasicBlock>(currBasicBlock)); } diff --git a/src/cfg/liveness-traversal.h b/src/cfg/liveness-traversal.h index 15e41428e..f27c43f8e 100644 --- a/src/cfg/liveness-traversal.h +++ b/src/cfg/liveness-traversal.h @@ -15,16 +15,7 @@ */ // -// Convert the AST to a CFG, while traversing it. -// -// Note that this is not the same as the relooper CFG. The relooper is -// designed for compilation to an AST, this is for processing. There is -// no built-in support for transforming this CFG into the AST back -// again, it is just metadata on the side for computation purposes. -// -// Usage: As the traversal proceeds, you can note information and add it to -// the current basic block using currBasicBlock, on the contents -// property, whose type is user-defined. +// Computes liveness information for locals. // #ifndef liveness_traversal_h @@ -48,7 +39,7 @@ typedef SortedVector LocalSet; // A liveness-relevant action. Supports a get, a set, or an // "other" which can be used for other purposes, to mark // their position in a block -struct Action { +struct LivenessAction { enum What { Get = 0, Set = 1, @@ -59,12 +50,12 @@ struct Action { Expression** origin; // the origin bool effective; // whether a store is actually effective, i.e., may be read - Action(What what, Index index, Expression** origin) : what(what), index(index), origin(origin), effective(false) { + LivenessAction(What what, Index index, Expression** origin) : what(what), index(index), origin(origin), effective(false) { assert(what != Other); if (what == Get) assert((*origin)->is<GetLocal>()); if (what == Set) assert((*origin)->is<SetLocal>()); } - Action(Expression** origin) : what(Other), origin(origin) {} + LivenessAction(Expression** origin) : what(Other), origin(origin) {} bool isGet() { return what == Get; } bool isSet() { return what == Set; } @@ -74,7 +65,7 @@ struct Action { // information about liveness in a basic block struct Liveness { LocalSet start, end; // live locals at the start and end - std::vector<Action> actions; // actions occurring in this block + std::vector<LivenessAction> actions; // actions occurring in this block void dump(Function* func) { if (actions.empty()) return; @@ -103,7 +94,7 @@ struct LivenessWalker : public CFGWalker<SubType, VisitorType, Liveness> { *currp = Builder(*self->getModule()).replaceWithIdenticalType(curr); return; } - self->currBasicBlock->contents.actions.emplace_back(Action::Get, curr->index, currp); + self->currBasicBlock->contents.actions.emplace_back(LivenessAction::Get, curr->index, currp); } static void doVisitSetLocal(SubType* self, Expression** currp) { @@ -117,7 +108,7 @@ struct LivenessWalker : public CFGWalker<SubType, VisitorType, Liveness> { } return; } - self->currBasicBlock->contents.actions.emplace_back(Action::Set, curr->index, currp); + self->currBasicBlock->contents.actions.emplace_back(LivenessAction::Set, curr->index, currp); // if this is a copy, note it if (auto* get = self->getCopy(curr)) { // add 2 units, so that backedge prioritization can decide ties, but not much more @@ -204,7 +195,7 @@ struct LivenessWalker : public CFGWalker<SubType, VisitorType, Liveness> { return old != ret; } - void scanLivenessThroughActions(std::vector<Action>& actions, LocalSet& live) { + void scanLivenessThroughActions(std::vector<LivenessAction>& actions, LocalSet& live) { // move towards the front for (int i = int(actions.size()) - 1; i >= 0; i--) { auto& action = actions[i]; diff --git a/src/ir/LocalGraph.cpp b/src/ir/LocalGraph.cpp index 53c6a52e2..77f5906f0 100644 --- a/src/ir/LocalGraph.cpp +++ b/src/ir/LocalGraph.cpp @@ -20,250 +20,203 @@ #include <wasm-printing.h> #include <ir/find_all.h> #include <ir/local-graph.h> +#include <cfg/cfg-traversal.h> namespace wasm { -LocalGraph::LocalGraph(Function* func, Module* module) { - walkFunctionInModule(func, module); +namespace LocalGraphInternal { + +// A relevant action. Supports a get, a set, or an +// "other" which can be used for other purposes, to mark +// their position in a block +struct Action { + enum What { + Get = 0, + Set = 1 + }; + What what; + Index index; // the local index read or written + Expression* expr; // the expression itself + + Action(What what, Index index, Expression* expr) : what(what), index(index), expr(expr) { + if (what == Get) assert(expr->is<GetLocal>()); + if (what == Set) assert(expr->is<SetLocal>()); + } -#ifdef LOCAL_GRAPH_DEBUG - std::cout << "LocalGraph::dump\n"; - for (auto& pair : getSetses) { - auto* get = pair.first; - auto& sets = pair.second; - std::cout << "GET\n" << get << " is influenced by\n"; - for (auto* set : sets) { - std::cout << set << '\n'; + bool isGet() { return what == Get; } + bool isSet() { return what == Set; } +}; + +// information about a basic block +struct Info { + std::vector<Action> actions; // actions occurring in this block + std::vector<SetLocal*> lastSets; // for each index, the last set_local for it + + void dump(Function* func) { + if (actions.empty()) return; + std::cout << " actions:\n"; + for (auto& action : actions) { + std::cout << " " << (action.isGet() ? "get" : "set") << " " << func->getLocalName(action.index) << "\n"; } } -#endif -} +}; -void LocalGraph::computeInfluences() { - for (auto& pair : locations) { - auto* curr = pair.first; - if (auto* set = curr->dynCast<SetLocal>()) { - FindAll<GetLocal> findAll(set->value); - for (auto* get : findAll.list) { - getInfluences[get].insert(set); - } - } else { - auto* get = curr->cast<GetLocal>(); - for (auto* set : getSetses[get]) { - setInfluences[set].insert(get); - } - } +// flow helper class. flows the gets to their sets + +struct Flower : public CFGWalker<Flower, Visitor<Flower>, Info> { + LocalGraph::GetSetses& getSetses; + LocalGraph::Locations& locations; + + Flower(LocalGraph::GetSetses& getSetses, LocalGraph::Locations& locations, Function* func) : getSetses(getSetses), locations(locations) { + setFunction(func); + // create the CFG by walking the IR + CFGWalker<Flower, Visitor<Flower>, Info>::doWalkFunction(func); + // flow gets across blocks + flow(func); } -} -void LocalGraph::doWalkFunction(Function* func) { - numLocals = func->getNumLocals(); - if (numLocals == 0) return; // nothing to do - // We begin with each param being assigned from the incoming value, and the zero-init for the locals, - // so the initial state is the identity permutation - currMapping.resize(numLocals); - for (auto& set : currMapping) { - set = { nullptr }; + BasicBlock* makeBasicBlock() { + auto* ret = new BasicBlock(); + auto& lastSets = ret->contents.lastSets; + lastSets.resize(getFunction()->getNumLocals()); + std::fill(lastSets.begin(), lastSets.end(), nullptr); + return ret; } - PostWalker<LocalGraph>::walk(func->body); -} -// control flow + // cfg traversal work -void LocalGraph::visitBlock(Block* curr) { - if (curr->name.is() && breakMappings.find(curr->name) != breakMappings.end()) { - auto& infos = breakMappings[curr->name]; - infos.emplace_back(std::move(currMapping)); - currMapping = std::move(merge(infos)); - breakMappings.erase(curr->name); + static void doVisitGetLocal(Flower* self, Expression** currp) { + auto* curr = (*currp)->cast<GetLocal>(); + // if in unreachable code, skip + if (!self->currBasicBlock) return; + self->currBasicBlock->contents.actions.emplace_back(Action::Get, curr->index, curr); + self->locations[curr] = currp; } -} -void LocalGraph::finishIf() { - // that's it for this if, merge - std::vector<Mapping> breaks; - breaks.emplace_back(std::move(currMapping)); - breaks.emplace_back(std::move(mappingStack.back())); - mappingStack.pop_back(); - currMapping = std::move(merge(breaks)); -} - -void LocalGraph::afterIfCondition(LocalGraph* self, Expression** currp) { - self->mappingStack.push_back(self->currMapping); -} -void LocalGraph::afterIfTrue(LocalGraph* self, Expression** currp) { - auto* curr = (*currp)->cast<If>(); - if (curr->ifFalse) { - auto afterCondition = std::move(self->mappingStack.back()); - self->mappingStack.back() = std::move(self->currMapping); - self->currMapping = std::move(afterCondition); - } else { - self->finishIf(); + static void doVisitSetLocal(Flower* self, Expression** currp) { + auto* curr = (*currp)->cast<SetLocal>(); + // if in unreachable code, skip + if (!self->currBasicBlock) return; + self->currBasicBlock->contents.actions.emplace_back(Action::Set, curr->index, curr); + self->currBasicBlock->contents.lastSets[curr->index] = curr; + self->locations[curr] = currp; } -} -void LocalGraph::afterIfFalse(LocalGraph* self, Expression** currp) { - self->finishIf(); -} -void LocalGraph::beforeLoop(LocalGraph* self, Expression** currp) { - // save the state before entering the loop, for calculation later of the merge at the loop top - self->mappingStack.push_back(self->currMapping); - self->loopGetStack.push_back({}); -} -void LocalGraph::visitLoop(Loop* curr) { - if (curr->name.is() && breakMappings.find(curr->name) != breakMappings.end()) { - auto& infos = breakMappings[curr->name]; - infos.emplace_back(std::move(mappingStack.back())); - auto before = infos.back(); - auto& merged = merge(infos); - // every local we created a phi for requires us to update get_local operations in - // the loop - the branch back has means that gets in the loop have potentially - // more sets reaching them. - // we can detect this as follows: if a get of oldIndex has the same sets - // as the sets at the entrance to the loop, then it is affected by the loop - // header sets, and we can add to there sets that looped back - auto linkLoopTop = [&](Index i, Sets& getSets) { - auto& beforeSets = before[i]; - if (getSets.size() < beforeSets.size()) { - // the get trivially has fewer sets, so it overrode the loop entry sets - return; + + void flow(Function* func) { + auto numLocals = func->getNumLocals(); + std::vector<std::vector<GetLocal*>> allGets; + allGets.resize(numLocals); + std::unordered_set<BasicBlock*> seen; + std::vector<BasicBlock*> work; + for (auto& block : basicBlocks) { +#ifdef LOCAL_GRAPH_DEBUG + std::cout << "basic block " << block.get() << " :\n"; + for (auto& action : block->contents.actions) { + std::cout << " action: " << action.expr << '\n'; } - if (!std::includes(getSets.begin(), getSets.end(), - beforeSets.begin(), beforeSets.end())) { - // the get has not the same sets as in the loop entry - return; + for (auto* lastSet : block->contents.lastSets) { + std::cout << " last set " << lastSet << '\n'; } - // the get has the entry sets, so add any new ones - for (auto* set : merged[i]) { - getSets.insert(set); +#endif + // go through the block, finding each get and adding it to its index, + // and seeing how sets affect that + auto& actions = block->contents.actions; + // move towards the front, handling things as we go + for (int i = int(actions.size()) - 1; i >= 0; i--) { + auto& action = actions[i]; + auto index = action.index; + if (action.isGet()) { + allGets[index].push_back(action.expr->cast<GetLocal>()); + } else { + // this set is the only set for all those gets + auto* set = action.expr->cast<SetLocal>(); + auto& gets = allGets[index]; + for (auto* get : gets) { + getSetses[get].insert(set); + } + gets.clear(); + } } - }; - auto& gets = loopGetStack.back(); - for (auto* get : gets) { - linkLoopTop(get->index, getSetses[get]); - } - // and the same for the loop fallthrough: any local that still has the - // entry sets should also have the loop-back sets as well - for (Index i = 0; i < numLocals; i++) { - linkLoopTop(i, currMapping[i]); - } - // finally, breaks still in flight must be updated too - for (auto& iter : breakMappings) { - auto name = iter.first; - if (name == curr->name) continue; // skip our own (which is still in use) - auto& mappings = iter.second; - for (auto& mapping : mappings) { - for (Index i = 0; i < numLocals; i++) { - linkLoopTop(i, mapping[i]); + // if anything is left, we must flow it back through other blocks. we + // can do that for all gets as a whole, they will get the same results + for (Index index = 0; index < numLocals; index++) { + auto& gets = allGets[index]; + if (gets.empty()) continue; + work.push_back(block.get()); + seen.clear(); + // note that we may need to revisit the later parts of this initial + // block, if we are in a loop, so don't mark it as seen + while (!work.empty()) { + auto* curr = work.back(); + work.pop_back(); + // we have gone through this block; now we must handle flowing to + // the inputs + if (curr->in.empty()) { + if (curr == entry) { + // these receive a param or zero init value + for (auto* get : gets) { + getSetses[get].insert(nullptr); + } + } + } else { + for (auto* pred : curr->in) { + if (seen.count(pred)) continue; + seen.insert(pred); + auto* lastSet = pred->contents.lastSets[index]; + if (lastSet) { + // there is a set here, apply it, and stop the flow + for (auto* get : gets) { + getSetses[get].insert(lastSet); + } + } else { + // keep on flowing + work.push_back(pred); + } + } + } } + gets.clear(); } } - // now that we are done with using the mappings, erase our own - breakMappings.erase(curr->name); - } - mappingStack.pop_back(); - loopGetStack.pop_back(); -} -void LocalGraph::visitBreak(Break* curr) { - if (curr->condition) { - breakMappings[curr->name].emplace_back(currMapping); - } else { - breakMappings[curr->name].emplace_back(std::move(currMapping)); - setUnreachable(currMapping); - } -} -void LocalGraph::visitSwitch(Switch* curr) { - std::set<Name> all; - for (auto target : curr->targets) { - all.insert(target); } - all.insert(curr->default_); - for (auto target : all) { - breakMappings[target].emplace_back(currMapping); - } - setUnreachable(currMapping); -} -void LocalGraph::visitReturn(Return *curr) { - setUnreachable(currMapping); -} -void LocalGraph::visitUnreachable(Unreachable *curr) { - setUnreachable(currMapping); -} +}; -// local usage +} // namespace LocalGraphInternal -void LocalGraph::visitGetLocal(GetLocal* curr) { - assert(currMapping.size() == numLocals); - assert(curr->index < numLocals); - for (auto& loopGets : loopGetStack) { - loopGets.push_back(curr); - } - // current sets are our sets - getSetses[curr] = currMapping[curr->index]; - locations[curr] = getCurrentPointer(); -} -void LocalGraph::visitSetLocal(SetLocal* curr) { - assert(currMapping.size() == numLocals); - assert(curr->index < numLocals); - // current sets are just this set - currMapping[curr->index] = { curr }; // TODO optimize? - locations[curr] = getCurrentPointer(); -} +// LocalGraph implementation -// traversal +LocalGraph::LocalGraph(Function* func) { + LocalGraphInternal::Flower flower(getSetses, locations, func); -void LocalGraph::scan(LocalGraph* self, Expression** currp) { - if (auto* iff = (*currp)->dynCast<If>()) { - // if needs special handling - if (iff->ifFalse) { - self->pushTask(LocalGraph::afterIfFalse, currp); - self->pushTask(LocalGraph::scan, &iff->ifFalse); +#ifdef LOCAL_GRAPH_DEBUG + std::cout << "LocalGraph::dump\n"; + for (auto& pair : getSetses) { + auto* get = pair.first; + auto& sets = pair.second; + std::cout << "GET\n" << get << " is influenced by\n"; + for (auto* set : sets) { + std::cout << set << '\n'; } - self->pushTask(LocalGraph::afterIfTrue, currp); - self->pushTask(LocalGraph::scan, &iff->ifTrue); - self->pushTask(LocalGraph::afterIfCondition, currp); - self->pushTask(LocalGraph::scan, &iff->condition); - } else { - PostWalker<LocalGraph>::scan(self, currp); } - - // loops need pre-order visiting too - if ((*currp)->is<Loop>()) { - self->pushTask(LocalGraph::beforeLoop, currp); - } -} - -// helpers - -void LocalGraph::setUnreachable(Mapping& mapping) { - mapping.resize(numLocals); // may have been emptied by a move - mapping[0].clear(); -} - -bool LocalGraph::isUnreachable(Mapping& mapping) { - // we must have some set for each index, if only the zero init, so empty means we emptied it for unreachable code - return mapping[0].empty(); + std::cout << "total locations: " << locations.size() << '\n'; +#endif } -// merges a bunch of infos into one. -// if we need phis, writes them into the provided vector. the caller should -// ensure those are placed in the right location -LocalGraph::Mapping& LocalGraph::merge(std::vector<Mapping>& mappings) { - assert(mappings.size() > 0); - auto& out = mappings[0]; - if (mappings.size() == 1) { - return out; - } - // merge into the first - for (Index j = 1; j < mappings.size(); j++) { - auto& other = mappings[j]; - for (Index i = 0; i < numLocals; i++) { - auto& outSets = out[i]; - for (auto* set : other[i]) { - outSets.insert(set); +void LocalGraph::computeInfluences() { + for (auto& pair : locations) { + auto* curr = pair.first; + if (auto* set = curr->dynCast<SetLocal>()) { + FindAll<GetLocal> findAll(set->value); + for (auto* get : findAll.list) { + getInfluences[get].insert(set); + } + } else { + auto* get = curr->cast<GetLocal>(); + for (auto* set : getSetses[get]) { + setInfluences[set].insert(get); } } } - return out; } } // namespace wasm diff --git a/src/ir/local-graph.h b/src/ir/local-graph.h index ea27fa1fb..6a8f1b2af 100644 --- a/src/ir/local-graph.h +++ b/src/ir/local-graph.h @@ -27,21 +27,23 @@ namespace wasm { // (see the SSA pass for actually creating new local indexes based // on this). // -// TODO: the algorithm here is pretty simple, but also pretty slow, -// we should optimize it. -struct LocalGraph : public PostWalker<LocalGraph> { +struct LocalGraph { // main API // the constructor computes getSetses, the sets affecting each get - LocalGraph(Function* func, Module* module); + LocalGraph(Function* func); // the set_locals relevant for an index or a get. typedef std::set<SetLocal*> Sets; + typedef std::map<GetLocal*, Sets> GetSetses; + + typedef std::map<Expression*, Expression**> Locations; + // externally useful information - std::map<GetLocal*, Sets> getSetses; // the sets affecting each get. a nullptr set means the initial + GetSetses getSetses; // the sets affecting each get. a nullptr set means the initial // value (0 for a var, the received value for a param) - std::map<Expression*, Expression**> locations; // where each get and set is (for easy replacing) + Locations locations; // where each get and set is (for easy replacing) // optional computation: compute the influence graphs between sets and gets // (useful for algorithms that propagate changes) @@ -50,58 +52,6 @@ struct LocalGraph : public PostWalker<LocalGraph> { std::unordered_map<SetLocal*, std::unordered_set<GetLocal*>> setInfluences; // for each set, the gets whose values are influenced by that set void computeInfluences(); - -private: - // we map local index => the set_locals for that index. - // a nullptr set means there is a virtual set, from a param - // initial value or the zero init initial value. - typedef std::vector<Sets> Mapping; - - // internal state - Index numLocals; - Mapping currMapping; - std::vector<Mapping> mappingStack; // used in ifs, loops - std::map<Name, std::vector<Mapping>> breakMappings; // break target => infos that reach it - std::vector<std::vector<GetLocal*>> loopGetStack; // stack of loops, all the gets in each, so we can update them for back branches - -public: - void doWalkFunction(Function* func); - - // control flow - - void visitBlock(Block* curr); - - void finishIf(); - - static void afterIfCondition(LocalGraph* self, Expression** currp); - static void afterIfTrue(LocalGraph* self, Expression** currp); - static void afterIfFalse(LocalGraph* self, Expression** currp); - static void beforeLoop(LocalGraph* self, Expression** currp); - void visitLoop(Loop* curr); - void visitBreak(Break* curr); - void visitSwitch(Switch* curr); - void visitReturn(Return *curr); - void visitUnreachable(Unreachable *curr); - - // local usage - - void visitGetLocal(GetLocal* curr); - void visitSetLocal(SetLocal* curr); - - // traversal - - static void scan(LocalGraph* self, Expression** currp); - - // helpers - - void setUnreachable(Mapping& mapping); - - bool isUnreachable(Mapping& mapping); - - // merges a bunch of infos into one. - // if we need phis, writes them into the provided vector. the caller should - // ensure those are placed in the right location - Mapping& merge(std::vector<Mapping>& mappings); }; } // namespace wasm diff --git a/src/passes/CoalesceLocals.cpp b/src/passes/CoalesceLocals.cpp index ae9d9246e..b6c96902b 100644 --- a/src/passes/CoalesceLocals.cpp +++ b/src/passes/CoalesceLocals.cpp @@ -103,7 +103,7 @@ void CoalesceLocals::increaseBackEdgePriorities() { auto* arrivingBlock = in[i]; if (arrivingBlock->out.size() > 1) continue; // we just want unconditional branches to the loop top, true phi fragments for (auto& action : arrivingBlock->contents.actions) { - if (action.what == Action::Set) { + if (action.isSet()) { auto* set = (*action.origin)->cast<SetLocal>(); if (auto* get = getCopy(set)) { // this is indeed a copy, add to the cost (default cost is 2, so this adds 50%, and can mostly break ties) diff --git a/src/passes/MergeLocals.cpp b/src/passes/MergeLocals.cpp index 31be495c1..8dcaa0cb9 100644 --- a/src/passes/MergeLocals.cpp +++ b/src/passes/MergeLocals.cpp @@ -96,7 +96,7 @@ struct MergeLocals : public WalkerPass<PostWalker<MergeLocals, UnifiedExpression void optimizeCopies() { if (copies.empty()) return; // compute all dependencies - LocalGraph preGraph(getFunction(), getModule()); + LocalGraph preGraph(getFunction()); preGraph.computeInfluences(); // optimize each copy std::unordered_map<SetLocal*, SetLocal*> optimizedToCopy, optimizedToTrivial; @@ -168,7 +168,7 @@ struct MergeLocals : public WalkerPass<PostWalker<MergeLocals, UnifiedExpression // if one does not work, we need to undo all its siblings (don't extend // the live range unless we are definitely removing a conflict, same // logic as before). - LocalGraph postGraph(getFunction(), getModule()); + LocalGraph postGraph(getFunction()); postGraph.computeInfluences(); for (auto& pair : optimizedToCopy) { auto* copy = pair.first; diff --git a/src/passes/Precompute.cpp b/src/passes/Precompute.cpp index f4c20d0d0..68133edca 100644 --- a/src/passes/Precompute.cpp +++ b/src/passes/Precompute.cpp @@ -120,7 +120,7 @@ struct Precompute : public WalkerPass<PostWalker<Precompute, UnifiedExpressionVi // things that use locals that are known to be constant. otherwise, // we just look at what is immediately before us if (propagate) { - optimizeLocals(func, getModule()); + optimizeLocals(func); } // do the main and final walk over everything super::doWalkFunction(func); @@ -210,13 +210,13 @@ private: return flow.value; } - void optimizeLocals(Function* func, Module* module) { + void optimizeLocals(Function* func) { // 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). // compute all dependencies - LocalGraph localGraph(func, module); + LocalGraph localGraph(func); localGraph.computeInfluences(); // prepare the work list. we add things here that might change to a constant // initially, that means everything diff --git a/src/passes/SSAify.cpp b/src/passes/SSAify.cpp index b2e75fce3..5271087a9 100644 --- a/src/passes/SSAify.cpp +++ b/src/passes/SSAify.cpp @@ -57,7 +57,7 @@ struct SSAify : public Pass { void runOnFunction(PassRunner* runner, Module* module_, Function* func_) override { module = module_; func = func_; - LocalGraph graph(func, module); + LocalGraph graph(func); // create new local indexes, one for each set createNewIndexes(graph); // we now know the sets for each get, and can compute get indexes and handle phis |