diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/passes/SSAify.cpp | 57 | ||||
-rw-r--r-- | src/passes/pass.cpp | 6 | ||||
-rw-r--r-- | src/passes/passes.h | 1 |
3 files changed, 58 insertions, 6 deletions
diff --git a/src/passes/SSAify.cpp b/src/passes/SSAify.cpp index 9e6ff2de2..1ed3b976f 100644 --- a/src/passes/SSAify.cpp +++ b/src/passes/SSAify.cpp @@ -27,6 +27,26 @@ // TODO: consider adding a "proper" phi node to the AST, that passes // can utilize // +// There is also a "no-merge" variant of this pass. That will ignore +// sets leading to merges, that is, it only creates new SSA indexes +// for sets whose gets have just that set, e.g. +// +// x = .. +// f(x, x) +// x = .. +// g(x, x) +// => +// x = .. +// f(x, x) +// x' = .. +// g(x', x') +// +// This "untangles" local indexes in a way that helps other passes, +// while not creating copies with overlapping lifetimes that can +// lead to a code size increase. In particular, the new variables +// added by ssa-nomerge can be easily removed by the coalesce-locals +// pass. +// #include <iterator> @@ -49,7 +69,11 @@ static SetLocal IMPOSSIBLE_SET; struct SSAify : public Pass { bool isFunctionParallel() override { return true; } - Pass* create() override { return new SSAify; } + Pass* create() override { return new SSAify(allowMerges); } + + SSAify(bool allowMerges) : allowMerges(allowMerges) {} + + bool allowMerges; Module* module; Function* func; @@ -59,21 +83,37 @@ struct SSAify : public Pass { module = module_; func = func_; LocalGraph graph(func); + graph.computeInfluences(); + graph.computeSSAIndexes(); // create new local indexes, one for each set - createNewIndexes(); + createNewIndexes(graph); // we now know the sets for each get, and can compute get indexes and handle phis computeGetsAndPhis(graph); // add prepends to function addPrepends(); } - void createNewIndexes() { + void createNewIndexes(LocalGraph& graph) { FindAll<SetLocal> sets(func->body); for (auto* set : sets.list) { - set->index = addLocal(func->getLocalType(set->index)); + // Indexes already in SSA form do not need to be modified - there is already + // just one set for that index. Otherwise, use a new index, unless merges + // are disallowed. + if (!graph.isSSA(set->index) && (allowMerges || !hasMerges(set, graph))) { + set->index = addLocal(func->getLocalType(set->index)); + } } } + bool hasMerges(SetLocal* set, LocalGraph& graph) { + for (auto* get : graph.setInfluences[set]) { + if (graph.getSetses[get].size() > 1) { + return true; + } + } + return false; + } + void computeGetsAndPhis(LocalGraph& graph) { FindAll<GetLocal> gets(func->body); for (auto* get : gets.list) { @@ -97,6 +137,7 @@ struct SSAify : public Pass { } continue; } + if (!allowMerges) continue; // more than 1 set, need a phi: a new local written to at each of the sets auto new_ = addLocal(get->type); auto old = get->index; @@ -154,8 +195,12 @@ struct SSAify : public Pass { } }; -Pass *createSSAifyPass() { - return new SSAify(); +Pass* createSSAifyPass() { + return new SSAify(true); +} + +Pass* createSSAifyNoMergePass() { + return new SSAify(false); } } // namespace wasm diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp index 7f8e09bbf..82a804eb5 100644 --- a/src/passes/pass.cpp +++ b/src/passes/pass.cpp @@ -135,6 +135,7 @@ void PassRegistry::registerPasses() { registerPass("souperify-single-use", "emit Souper IR in text form (single-use nodes only)", createSouperifySingleUsePass); registerPass("spill-pointers", "spill pointers to the C stack (useful for Boehm-style GC)", createSpillPointersPass); registerPass("ssa", "ssa-ify variables so that they have a single assignment", createSSAifyPass); + registerPass("ssa-nomerge", "ssa-ify variables so that they have a single assignment, ignoring merges", createSSAifyNoMergePass); registerPass("strip", "deprecated; same as strip-debug", createStripDebugPass); registerPass("strip-debug", "strip debug info (including the names section)", createStripDebugPass); registerPass("strip-producers", "strip the wasm producers section", createStripProducersPass); @@ -153,6 +154,11 @@ void PassRunner::addDefaultOptimizationPasses() { } void PassRunner::addDefaultFunctionOptimizationPasses() { + // Untangling to semi-ssa form is helpful (but best to ignore merges + // so as to not introduce new copies). + if (options.optimizeLevel >= 3 || options.shrinkLevel >= 1) { + add("ssa-nomerge"); + } // if we are willing to work very very hard, flatten the IR and do opts // that depend on flat IR if (options.optimizeLevel >= 4) { diff --git a/src/passes/passes.h b/src/passes/passes.h index ac7126bd4..5ad54f705 100644 --- a/src/passes/passes.h +++ b/src/passes/passes.h @@ -93,6 +93,7 @@ Pass* createSouperifyPass(); Pass* createSouperifySingleUsePass(); Pass* createSpillPointersPass(); Pass* createSSAifyPass(); +Pass* createSSAifyNoMergePass(); Pass* createTrapModeClamp(); Pass* createTrapModeJS(); Pass* createUnteePass(); |