summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/passes/SSAify.cpp57
-rw-r--r--src/passes/pass.cpp6
-rw-r--r--src/passes/passes.h1
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();