summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/passes/CoalesceLocals.cpp98
-rw-r--r--test/passes/coalesce-locals-learning.txt680
-rw-r--r--test/passes/coalesce-locals-learning.wast594
3 files changed, 1365 insertions, 7 deletions
diff --git a/src/passes/CoalesceLocals.cpp b/src/passes/CoalesceLocals.cpp
index 311280792..bb9b97073 100644
--- a/src/passes/CoalesceLocals.cpp
+++ b/src/passes/CoalesceLocals.cpp
@@ -22,6 +22,7 @@
//
+#include <algorithm>
#include <memory>
#include <unordered_set>
@@ -29,6 +30,7 @@
#include "pass.h"
#include "ast_utils.h"
#include "cfg/cfg-traversal.h"
+#include "support/learning.h"
#ifdef CFG_PROFILE
#include "support/timing.h"
#endif
@@ -351,7 +353,6 @@ void CoalesceLocals::scanLivenessThroughActions(std::vector<Action>& actions, Lo
void CoalesceLocals::pickIndicesFromOrder(std::vector<Index>& order, std::vector<Index>& indices) {
// simple greedy coloring
- // TODO: multiple orderings
// TODO: take into account eliminated copies
// TODO: take into account distribution (99-1 is better than 50-50 with two registers, for gzip)
std::vector<WasmType> types;
@@ -365,6 +366,7 @@ void CoalesceLocals::pickIndicesFromOrder(std::vector<Index>& order, std::vector
auto numParams = getFunction()->getNumParams();
Index i = 0;
for (; i < numParams; i++) {
+ assert(order[i] == i); // order must leave the params in place
indices[i] = i;
types[i] = getFunction()->getLocalType(i);
for (size_t j = 0; j < numLocals; j++) {
@@ -373,21 +375,22 @@ void CoalesceLocals::pickIndicesFromOrder(std::vector<Index>& order, std::vector
nextFree++;
}
for (; i < numLocals; i++) {
+ Index actual = order[i];
Index found = -1;
for (Index j = 0; j < nextFree; j++) {
- if (!newInterferences[j * numLocals + i] && getFunction()->getLocalType(i) == types[j]) {
- indices[i] = found = j;
+ if (!newInterferences[j * numLocals + actual] && getFunction()->getLocalType(actual) == types[j]) {
+ indices[actual] = found = j;
break;
}
}
if (found == Index(-1)) {
- indices[i] = found = nextFree;
- types[found] = getFunction()->getLocalType(i);
+ indices[actual] = found = nextFree;
+ types[found] = getFunction()->getLocalType(actual);
nextFree++;
}
// merge new interferences for the new index
for (size_t j = 0; j < numLocals; j++) {
- newInterferences[found * numLocals + j] = newInterferences[found * numLocals + j] | interferes(i, j);
+ newInterferences[found * numLocals + j] = newInterferences[found * numLocals + j] | interferes(actual, j);
}
}
}
@@ -443,6 +446,87 @@ void CoalesceLocals::applyIndices(std::vector<Index>& indices, Expression* root)
getFunction()->localIndices.clear();
}
-static RegisterPass<CoalesceLocals> registerPass("coalesce-locals", "reduce # of locals by coalescing");
+struct CoalesceLocalsWithLearning : public CoalesceLocals {
+ virtual CoalesceLocals* create() override { return new CoalesceLocalsWithLearning; }
+
+ virtual void pickIndices(std::vector<Index>& indices) override;
+};
+
+void CoalesceLocalsWithLearning::pickIndices(std::vector<Index>& indices) {
+ if (getFunction()->getNumVars() <= 1) {
+ // nothing to think about here
+ CoalesceLocals::pickIndices(indices);
+ return;
+ }
+
+ struct Order : public std::vector<Index> {
+ void setFitness(Index f) { fitness = f; }
+ Index getFitness() { return fitness; }
+ void dump(std::string text) {
+ std::cout << text + ": ( ";
+ for (Index i = 0; i < size(); i++) std::cout << (*this)[i] << " ";
+ std::cout << ")\n";
+ std::cout << "of quality: " << getFitness() << "\n";
+ }
+ private:
+ Index fitness;
+ };
+
+ struct Generator {
+ Generator(CoalesceLocalsWithLearning* parent) : parent(parent), noise(42) {}
+
+ Order* makeRandom() {
+ auto* ret = new Order;
+ ret->resize(parent->numLocals);
+ for (Index i = 0; i < parent->numLocals; i++) {
+ (*ret)[i] = i;
+ }
+ if (first) {
+ // as the first guess, use the natural order. this is not arbitrary for two reasons.
+ // first, there may be an inherent order in the input (frequent indices are lower,
+ // etc.). second, by ensuring we start with the natural order, we ensure we are at
+ // least as good as the non-learning variant.
+ first = false;
+ } else {
+ // leave params alone, shuffle the rest
+ std::random_shuffle(ret->begin() + parent->getFunction()->getNumParams(), ret->end(), [this](int i) {
+ return noise() % i;
+ });
+ }
+ // apply the order
+ std::vector<Index> indices; // the phenotype
+ parent->pickIndicesFromOrder(*ret, indices);
+ auto maxIndex = *std::max_element(indices.begin(), indices.end());
+ assert(maxIndex <= parent->numLocals);
+ auto fitness = parent->numLocals - maxIndex; // higher fitness is better
+ ret->setFitness(fitness);
+#ifdef CFG_LEARN_DEBUG
+ ret->dump("new one");
+#endif
+ // all done
+ return ret;
+ }
+
+ private:
+ CoalesceLocalsWithLearning* parent;
+ std::mt19937 noise;
+ bool first = true;
+ };
+
+ Generator generator(this);
+ GeneticLearner<Order, Index, Generator> learner(generator, 10);
+ // TODO: run some generations
+ auto* best = learner.getBest();
+#ifdef CFG_LEARN_DEBUG
+ best->dump("new one");
+#endif
+ pickIndicesFromOrder(*best, indices); // TODO: cache indices in Orders, at the cost of more memory?
+}
+
+// declare passes
+
+static RegisterPass<CoalesceLocals> registerPass1("coalesce-locals", "reduce # of locals by coalescing");
+
+static RegisterPass<CoalesceLocalsWithLearning> registerPass2("coalesce-locals-learning", "reduce # of locals by coalescing and learning");
} // namespace wasm
diff --git a/test/passes/coalesce-locals-learning.txt b/test/passes/coalesce-locals-learning.txt
new file mode 100644
index 000000000..03e58347a
--- /dev/null
+++ b/test/passes/coalesce-locals-learning.txt
@@ -0,0 +1,680 @@
+(module
+ (memory 10)
+ (type $FUNCSIG$iiii (func (param i32 i32 i32) (result i32)))
+ (type $FUNCSIG$iii (func (param i32 i32) (result i32)))
+ (import $_emscripten_autodebug_i32 "env" "_emscripten_autodebug_i32" (param i32 i32) (result i32))
+ (func $nothing-to-do
+ (local $0 i32)
+ (nop)
+ )
+ (func $merge
+ (local $0 i32)
+ (nop)
+ )
+ (func $leave-type
+ (local $0 i32)
+ (local $1 f32)
+ (nop)
+ )
+ (func $leave-interfere
+ (local $0 i32)
+ (local $1 i32)
+ (set_local $0
+ (i32.const 0)
+ )
+ (set_local $1
+ (i32.const 0)
+ )
+ (get_local $0)
+ (get_local $1)
+ )
+ (func $almost-interfere
+ (local $0 i32)
+ (set_local $0
+ (i32.const 0)
+ )
+ (get_local $0)
+ (set_local $0
+ (i32.const 0)
+ )
+ (get_local $0)
+ )
+ (func $redundant-copy
+ (local $0 i32)
+ (set_local $0
+ (i32.const 0)
+ )
+ (get_local $0)
+ (get_local $0)
+ )
+ (func $ineffective-store
+ (local $0 i32)
+ (i32.const 0)
+ (set_local $0
+ (i32.const 0)
+ )
+ (get_local $0)
+ )
+ (func $block
+ (local $0 i32)
+ (block $block0
+ (set_local $0
+ (i32.const 0)
+ )
+ )
+ (get_local $0)
+ )
+ (func $see-both-sides
+ (local $0 i32)
+ (local $1 i32)
+ (set_local $0
+ (i32.const 0)
+ )
+ (block $block0
+ (set_local $1
+ (i32.const 0)
+ )
+ )
+ (get_local $0)
+ (get_local $1)
+ )
+ (func $see-br-and-ignore-dead
+ (local $0 i32)
+ (set_local $0
+ (i32.const 0)
+ )
+ (block $block
+ (br $block)
+ (i32.const 0)
+ (get_local $0)
+ (i32.const -1)
+ )
+ (get_local $0)
+ )
+ (func $see-block-body
+ (local $0 i32)
+ (local $1 i32)
+ (set_local $0
+ (i32.const 0)
+ )
+ (block $block
+ (set_local $1
+ (i32.const 0)
+ )
+ (get_local $1)
+ (br $block)
+ )
+ (get_local $0)
+ )
+ (func $zero-init
+ (local $0 i32)
+ (local $1 i32)
+ (get_local $0)
+ (get_local $1)
+ )
+ (func $multi
+ (local $0 i32)
+ (local $1 i32)
+ (get_local $0)
+ (get_local $1)
+ )
+ (func $if-else
+ (local $0 i32)
+ (local $1 i32)
+ (if
+ (i32.const 0)
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ (func $if-else-parallel
+ (local $0 i32)
+ (if
+ (i32.const 0)
+ (block $block1
+ (set_local $0
+ (i32.const 0)
+ )
+ (get_local $0)
+ )
+ (block $block3
+ (set_local $0
+ (i32.const 1)
+ )
+ (get_local $0)
+ )
+ )
+ )
+ (func $if-else-after
+ (local $0 i32)
+ (local $1 i32)
+ (if
+ (i32.const 0)
+ (set_local $0
+ (i32.const 0)
+ )
+ (set_local $1
+ (i32.const 1)
+ )
+ )
+ (get_local $0)
+ (get_local $1)
+ )
+ (func $if-else-through
+ (local $0 i32)
+ (local $1 i32)
+ (set_local $0
+ (i32.const 0)
+ )
+ (set_local $1
+ (i32.const 1)
+ )
+ (if
+ (i32.const 0)
+ (i32.const 1)
+ (i32.const 2)
+ )
+ (get_local $0)
+ (get_local $1)
+ )
+ (func $if-through
+ (local $0 i32)
+ (local $1 i32)
+ (set_local $0
+ (i32.const 0)
+ )
+ (set_local $1
+ (i32.const 1)
+ )
+ (if
+ (i32.const 0)
+ (i32.const 1)
+ )
+ (get_local $0)
+ (get_local $1)
+ )
+ (func $if-through2
+ (local $0 i32)
+ (local $1 i32)
+ (set_local $0
+ (i32.const 0)
+ )
+ (if
+ (i32.const 0)
+ (set_local $1
+ (i32.const 1)
+ )
+ )
+ (get_local $0)
+ (get_local $1)
+ )
+ (func $if-through2
+ (local $0 i32)
+ (local $1 i32)
+ (set_local $0
+ (i32.const 0)
+ )
+ (if
+ (i32.const 0)
+ (block $block1
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+ (func $if2
+ (local $0 i32)
+ (local $1 i32)
+ (if
+ (set_local $0
+ (i32.const 0)
+ )
+ (block $block1
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+ (func $if3
+ (local $0 i32)
+ (local $1 i32)
+ (if
+ (i32.const 0)
+ (block $block1
+ (set_local $0
+ (i32.const 0)
+ )
+ (get_local $0)
+ )
+ )
+ (get_local $1)
+ )
+ (func $if4
+ (local $0 i32)
+ (if
+ (i32.const 0)
+ (block $block1
+ (set_local $0
+ (i32.const 0)
+ )
+ (get_local $0)
+ (set_local $0
+ (i32.const 1)
+ )
+ )
+ )
+ (get_local $0)
+ )
+ (func $if5
+ (local $0 i32)
+ (local $1 i32)
+ (if
+ (i32.const 0)
+ (block $block1
+ (get_local $0)
+ (set_local $1
+ (i32.const 1)
+ )
+ )
+ )
+ (get_local $1)
+ )
+ (func $loop
+ (local $0 i32)
+ (local $1 i32)
+ (loop $out $in
+ (get_local $0)
+ (set_local $0
+ (i32.const 0)
+ )
+ (get_local $1)
+ (br $in)
+ )
+ )
+ (func $interfere-in-dead
+ (local $0 i32)
+ (block $block
+ (br $block)
+ (get_local $0)
+ (get_local $0)
+ )
+ )
+ (func $interfere-in-dead2
+ (local $0 i32)
+ (block $block
+ (unreachable)
+ (get_local $0)
+ (get_local $0)
+ )
+ )
+ (func $interfere-in-dead3
+ (local $0 i32)
+ (block $block
+ (return)
+ (get_local $0)
+ (get_local $0)
+ )
+ )
+ (func $params (param $0 i32) (param $1 f32)
+ (local $2 i32)
+ (local $3 i32)
+ (get_local $0)
+ (get_local $2)
+ (get_local $3)
+ )
+ (func $interfere-in-dead
+ (local $0 i32)
+ (local $1 i32)
+ (block $block
+ (br_if $block
+ (i32.const 0)
+ )
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ (func $switch
+ (local $0 i32)
+ (local $1 i32)
+ (local $2 i32)
+ (block $switch$def
+ (block $switch-case$1
+ (block $switch-case$2
+ (br_table $switch-case$1 $switch-case$2 $switch-case$1 $switch-case$1 $switch$def
+ (i32.const 100)
+ )
+ (get_local $0)
+ )
+ (get_local $0)
+ )
+ (get_local $1)
+ )
+ (get_local $2)
+ )
+ (func $greedy-can-be-happy
+ (local $0 i32)
+ (local $1 i32)
+ (if
+ (i32.const 0)
+ (if
+ (i32.const 1)
+ (if
+ (i32.const 2)
+ (block $block3
+ (set_local $0
+ (i32.const 100)
+ )
+ (set_local $1
+ (i32.const 101)
+ )
+ (get_local $0)
+ (get_local $1)
+ )
+ (block $block5
+ (set_local $0
+ (i32.const 102)
+ )
+ (set_local $1
+ (i32.const 103)
+ )
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ (if
+ (i32.const 3)
+ (block $block8
+ (set_local $0
+ (i32.const 104)
+ )
+ (set_local $1
+ (i32.const 105)
+ )
+ (get_local $0)
+ (get_local $1)
+ )
+ (block $block10
+ (set_local $0
+ (i32.const 106)
+ )
+ (set_local $1
+ (i32.const 107)
+ )
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+ (if
+ (i32.const 4)
+ (block $block13
+ (set_local $0
+ (i32.const 108)
+ )
+ (set_local $1
+ (i32.const 109)
+ )
+ (get_local $0)
+ (get_local $1)
+ )
+ (block $block15
+ (set_local $0
+ (i32.const 110)
+ )
+ (set_local $1
+ (i32.const 111)
+ )
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+ )
+ (func $greedy-can-be-sad
+ (local $0 i32)
+ (local $1 i32)
+ (if
+ (i32.const 0)
+ (if
+ (i32.const 1)
+ (if
+ (i32.const 2)
+ (block $block3
+ (set_local $1
+ (i32.const 100)
+ )
+ (set_local $0
+ (i32.const 101)
+ )
+ (get_local $1)
+ (get_local $0)
+ )
+ (block $block5
+ (set_local $1
+ (i32.const 102)
+ )
+ (set_local $0
+ (i32.const 103)
+ )
+ (get_local $1)
+ (get_local $0)
+ )
+ )
+ (if
+ (i32.const 3)
+ (block $block8
+ (set_local $1
+ (i32.const 104)
+ )
+ (set_local $0
+ (i32.const 105)
+ )
+ (get_local $1)
+ (get_local $0)
+ )
+ (block $block10
+ (set_local $1
+ (i32.const 106)
+ )
+ (set_local $0
+ (i32.const 107)
+ )
+ (get_local $1)
+ (get_local $0)
+ )
+ )
+ )
+ (if
+ (i32.const 4)
+ (block $block13
+ (set_local $1
+ (i32.const 108)
+ )
+ (set_local $0
+ (i32.const 109)
+ )
+ (get_local $1)
+ (get_local $0)
+ )
+ (block $block15
+ (set_local $1
+ (i32.const 110)
+ )
+ (set_local $0
+ (i32.const 111)
+ )
+ (get_local $1)
+ (get_local $0)
+ )
+ )
+ )
+ )
+ (func $_memcpy (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
+ (local $3 i32)
+ (if
+ (i32.ge_s
+ (get_local $2)
+ (i32.const 4096)
+ )
+ (get_local $0)
+ )
+ (set_local $3
+ (get_local $0)
+ )
+ (if
+ (i32.eq
+ (i32.and
+ (get_local $0)
+ (i32.const 3)
+ )
+ (i32.and
+ (get_local $1)
+ (i32.const 3)
+ )
+ )
+ (block $block2
+ (loop $while-out$0 $while-in$1
+ (if
+ (i32.eqz
+ (i32.and
+ (get_local $0)
+ (i32.const 3)
+ )
+ )
+ (br $while-out$0)
+ )
+ (block $block4
+ (if
+ (i32.eqz
+ (get_local $2)
+ )
+ (return
+ (get_local $3)
+ )
+ )
+ (i32.store8
+ (get_local $0)
+ (i32.load8_s
+ (get_local $1)
+ )
+ )
+ (set_local $0
+ (i32.add
+ (get_local $0)
+ (i32.const 1)
+ )
+ )
+ (set_local $1
+ (i32.add
+ (get_local $1)
+ (i32.const 1)
+ )
+ )
+ (set_local $2
+ (i32.sub
+ (get_local $2)
+ (i32.const 1)
+ )
+ )
+ )
+ (br $while-in$1)
+ )
+ (loop $while-out$2 $while-in$3
+ (if
+ (i32.eqz
+ (i32.ge_s
+ (get_local $2)
+ (i32.const 4)
+ )
+ )
+ (br $while-out$2)
+ )
+ (block $block7
+ (i32.store
+ (get_local $0)
+ (i32.load
+ (get_local $1)
+ )
+ )
+ (set_local $0
+ (i32.add
+ (get_local $0)
+ (i32.const 4)
+ )
+ )
+ (set_local $1
+ (i32.add
+ (get_local $1)
+ (i32.const 4)
+ )
+ )
+ (set_local $2
+ (i32.sub
+ (get_local $2)
+ (i32.const 4)
+ )
+ )
+ )
+ (br $while-in$3)
+ )
+ )
+ )
+ (loop $while-out$4 $while-in$5
+ (if
+ (i32.eqz
+ (i32.gt_s
+ (get_local $2)
+ (i32.const 0)
+ )
+ )
+ (br $while-out$4)
+ )
+ (block $block9
+ (i32.store8
+ (get_local $0)
+ (i32.load8_s
+ (get_local $1)
+ )
+ )
+ (set_local $0
+ (i32.add
+ (get_local $0)
+ (i32.const 1)
+ )
+ )
+ (set_local $1
+ (i32.add
+ (get_local $1)
+ (i32.const 1)
+ )
+ )
+ (set_local $2
+ (i32.sub
+ (get_local $2)
+ (i32.const 1)
+ )
+ )
+ )
+ (br $while-in$5)
+ )
+ (return
+ (get_local $3)
+ )
+ )
+ (func $this-is-effective-i-tell-you (param $0 i32)
+ (if
+ (i32.const -1)
+ (block $block1
+ (if
+ (i32.const 0)
+ (nop)
+ )
+ (set_local $0
+ (i32.const 1)
+ )
+ )
+ (nop)
+ )
+ (get_local $0)
+ )
+)
diff --git a/test/passes/coalesce-locals-learning.wast b/test/passes/coalesce-locals-learning.wast
new file mode 100644
index 000000000..a7152d16a
--- /dev/null
+++ b/test/passes/coalesce-locals-learning.wast
@@ -0,0 +1,594 @@
+(module
+ (memory 10)
+ (type $FUNCSIG$iiii (func (param i32 i32 i32) (result i32)))
+ (import $_emscripten_autodebug_i32 "env" "_emscripten_autodebug_i32" (param i32 i32) (result i32))
+ (table)
+ (func $nothing-to-do
+ (local $x i32)
+ )
+ (func $merge
+ (local $x i32)
+ (local $y i32)
+ )
+ (func $leave-type
+ (local $x i32)
+ (local $y f32)
+ )
+ (func $leave-interfere
+ (local $x i32)
+ (local $y i32)
+ (set_local $x (i32.const 0))
+ (set_local $y (i32.const 0))
+ (get_local $x)
+ (get_local $y)
+ )
+ (func $almost-interfere
+ (local $x i32)
+ (local $y i32)
+ (set_local $x (i32.const 0))
+ (get_local $x)
+ (set_local $y (i32.const 0))
+ (get_local $y)
+ )
+ (func $redundant-copy
+ (local $x i32)
+ (local $y i32)
+ (set_local $x (i32.const 0))
+ (set_local $y (get_local $x))
+ (get_local $y)
+ )
+ (func $ineffective-store
+ (local $x i32)
+ (set_local $x (i32.const 0))
+ (set_local $x (i32.const 0))
+ (get_local $x)
+ )
+ (func $block
+ (local $x i32)
+ (block
+ (set_local $x (i32.const 0))
+ )
+ (get_local $x)
+ )
+ (func $see-both-sides
+ (local $x i32)
+ (local $y i32)
+ (set_local $x (i32.const 0))
+ (block
+ (set_local $y (i32.const 0))
+ )
+ (get_local $x)
+ (get_local $y)
+ )
+ (func $see-br-and-ignore-dead
+ (local $x i32)
+ (local $y i32)
+ (set_local $x (i32.const 0))
+ (block $block
+ (br $block)
+ (set_local $y (i32.const 0))
+ (get_local $y)
+ (set_local $x (i32.const -1))
+ )
+ (get_local $x)
+ )
+ (func $see-block-body
+ (local $x i32)
+ (local $y i32)
+ (set_local $x (i32.const 0))
+ (block $block
+ (set_local $y (i32.const 0))
+ (get_local $y)
+ (br $block)
+ )
+ (get_local $x)
+ )
+ (func $zero-init
+ (local $x i32)
+ (local $y i32)
+ (get_local $x)
+ (get_local $y)
+ )
+ (func $multi
+ (local $x i32) ;; x is free, but y and z conflict
+ (local $y i32)
+ (local $z i32)
+ (get_local $y)
+ (get_local $z)
+ )
+ (func $if-else
+ (local $x i32)
+ (local $y i32)
+ (if ;; x and y conflict when they are merged into their shared predecessor
+ (i32.const 0)
+ (get_local $x)
+ (get_local $y)
+ )
+ )
+ (func $if-else-parallel
+ (local $x i32)
+ (local $y i32)
+ (if
+ (i32.const 0)
+ (block
+ (set_local $x (i32.const 0))
+ (get_local $x)
+ )
+ (block
+ (set_local $y (i32.const 1))
+ (get_local $y)
+ )
+ )
+ )
+ (func $if-else-after
+ (local $x i32)
+ (local $y i32)
+ (if
+ (i32.const 0)
+ (set_local $x (i32.const 0))
+ (set_local $y (i32.const 1))
+ )
+ (get_local $x)
+ (get_local $y)
+ )
+ (func $if-else-through
+ (local $x i32)
+ (local $y i32)
+ (set_local $x (i32.const 0))
+ (set_local $y (i32.const 1))
+ (if
+ (i32.const 0)
+ (i32.const 1)
+ (i32.const 2)
+ )
+ (get_local $x)
+ (get_local $y)
+ )
+ (func $if-through
+ (local $x i32)
+ (local $y i32)
+ (set_local $x (i32.const 0))
+ (set_local $y (i32.const 1))
+ (if
+ (i32.const 0)
+ (i32.const 1)
+ )
+ (get_local $x)
+ (get_local $y)
+ )
+ (func $if-through2
+ (local $x i32)
+ (local $y i32)
+ (set_local $x (i32.const 0))
+ (if
+ (i32.const 0)
+ (set_local $y (i32.const 1))
+ )
+ (get_local $x)
+ (get_local $y)
+ )
+ (func $if-through2
+ (local $x i32)
+ (local $y i32)
+ (set_local $x (i32.const 0))
+ (if
+ (i32.const 0)
+ (block
+ (get_local $x)
+ (get_local $y)
+ )
+ )
+ )
+ (func $if2
+ (local $x i32)
+ (local $y i32)
+ (if
+ (set_local $x (i32.const 0))
+ (block
+ (get_local $x)
+ (get_local $y)
+ )
+ )
+ )
+ (func $if3
+ (local $x i32)
+ (local $y i32)
+ (if
+ (i32.const 0)
+ (block
+ (set_local $x (i32.const 0))
+ (get_local $x)
+ )
+ )
+ (get_local $y)
+ )
+ (func $if4
+ (local $x i32)
+ (local $y i32)
+ (if
+ (i32.const 0)
+ (block
+ (set_local $x (i32.const 0))
+ (get_local $x)
+ (set_local $y (i32.const 1)) ;; we might not go through here, but it's ok
+ )
+ )
+ (get_local $y)
+ )
+ (func $if5
+ (local $x i32)
+ (local $y i32)
+ (if
+ (i32.const 0)
+ (block
+ (get_local $x) ;; we might go through here, and it causes interference
+ (set_local $y (i32.const 1))
+ )
+ )
+ (get_local $y)
+ )
+ (func $loop
+ (local $x i32)
+ (local $y i32)
+ (loop $out $in
+ (get_local $x)
+ (set_local $x (i32.const 0)) ;; effective, due to looping
+ (get_local $y)
+ (br $in)
+ )
+ )
+ (func $interfere-in-dead
+ (local $x i32)
+ (local $y i32)
+ (block $block
+ (br $block)
+ (get_local $x)
+ (get_local $y)
+ )
+ )
+ (func $interfere-in-dead2
+ (local $x i32)
+ (local $y i32)
+ (block $block
+ (unreachable)
+ (get_local $x)
+ (get_local $y)
+ )
+ )
+ (func $interfere-in-dead3
+ (local $x i32)
+ (local $y i32)
+ (block $block
+ (return)
+ (get_local $x)
+ (get_local $y)
+ )
+ )
+ (func $params (param $p i32) (param $q f32)
+ (local $x i32) ;; x is free, but others conflict
+ (local $y i32)
+ (local $z i32)
+ (local $w i32)
+ (get_local $y)
+ (get_local $z)
+ (get_local $w)
+ )
+ (func $interfere-in-dead
+ (local $x i32)
+ (local $y i32)
+ (block $block
+ (br_if $block (i32.const 0))
+ (get_local $x)
+ (get_local $y)
+ )
+ )
+ (func $switch
+ (local $x i32)
+ (local $y i32)
+ (local $z i32)
+ (local $w i32)
+ (block $switch$def
+ (block $switch-case$1
+ (block $switch-case$2
+ (br_table $switch-case$1 $switch-case$2 $switch-case$1 $switch-case$1 $switch$def
+ (i32.const 100)
+ )
+ (get_local $x) ;; unreachable
+ )
+ (get_local $y)
+ )
+ (get_local $z)
+ )
+ (get_local $w)
+ )
+ (func $greedy-can-be-happy
+ (local $x1 i32)
+ (local $x2 i32)
+ (local $x3 i32)
+ (local $y1 i32)
+ (local $y2 i32)
+ (local $y3 i32)
+ (if
+ (i32.const 0)
+ (if
+ (i32.const 1)
+ (if
+ (i32.const 2)
+ (block
+ (set_local $x1 (i32.const 100))
+ (set_local $y2 (i32.const 101))
+ (get_local $x1)
+ (get_local $y2)
+ )
+ (block
+ (set_local $x1 (i32.const 102))
+ (set_local $y3 (i32.const 103))
+ (get_local $x1)
+ (get_local $y3)
+ )
+ )
+ (if
+ (i32.const 3)
+ (block
+ (set_local $x2 (i32.const 104))
+ (set_local $y1 (i32.const 105))
+ (get_local $x2)
+ (get_local $y1)
+ )
+ (block
+ (set_local $x2 (i32.const 106))
+ (set_local $y3 (i32.const 107))
+ (get_local $x2)
+ (get_local $y3)
+ )
+ )
+ )
+ (if
+ (i32.const 4)
+ (block
+ (set_local $x3 (i32.const 108))
+ (set_local $y1 (i32.const 109))
+ (get_local $x3)
+ (get_local $y1)
+ )
+ (block
+ (set_local $x3 (i32.const 110))
+ (set_local $y2 (i32.const 111))
+ (get_local $x3)
+ (get_local $y2)
+ )
+ )
+ )
+ )
+ (func $greedy-can-be-sad
+ (local $x1 i32)
+ (local $y1 i32)
+ (local $x2 i32)
+ (local $y2 i32)
+ (local $x3 i32)
+ (local $y3 i32)
+ (if
+ (i32.const 0)
+ (if
+ (i32.const 1)
+ (if
+ (i32.const 2)
+ (block
+ (set_local $x1 (i32.const 100))
+ (set_local $y2 (i32.const 101))
+ (get_local $x1)
+ (get_local $y2)
+ )
+ (block
+ (set_local $x1 (i32.const 102))
+ (set_local $y3 (i32.const 103))
+ (get_local $x1)
+ (get_local $y3)
+ )
+ )
+ (if
+ (i32.const 3)
+ (block
+ (set_local $x2 (i32.const 104))
+ (set_local $y1 (i32.const 105))
+ (get_local $x2)
+ (get_local $y1)
+ )
+ (block
+ (set_local $x2 (i32.const 106))
+ (set_local $y3 (i32.const 107))
+ (get_local $x2)
+ (get_local $y3)
+ )
+ )
+ )
+ (if
+ (i32.const 4)
+ (block
+ (set_local $x3 (i32.const 108))
+ (set_local $y1 (i32.const 109))
+ (get_local $x3)
+ (get_local $y1)
+ )
+ (block
+ (set_local $x3 (i32.const 110))
+ (set_local $y2 (i32.const 111))
+ (get_local $x3)
+ (get_local $y2)
+ )
+ )
+ )
+ )
+ (func $_memcpy (param $i1 i32) (param $i2 i32) (param $i3 i32) (result i32)
+ (local $i4 i32)
+ (if
+ (i32.ge_s
+ (get_local $i3)
+ (i32.const 4096)
+ )
+ (get_local $i1)
+ (get_local $i2)
+ (get_local $i3)
+ (return)
+ )
+ (set_local $i4
+ (get_local $i1)
+ )
+ (if
+ (i32.eq
+ (i32.and
+ (get_local $i1)
+ (i32.const 3)
+ )
+ (i32.and
+ (get_local $i2)
+ (i32.const 3)
+ )
+ )
+ (block
+ (loop $while-out$0 $while-in$1
+ (if
+ (i32.eqz
+ (i32.and
+ (get_local $i1)
+ (i32.const 3)
+ )
+ )
+ (br $while-out$0)
+ )
+ (block
+ (if
+ (i32.eqz
+ (get_local $i3)
+ )
+ (return
+ (get_local $i4)
+ )
+ )
+ (i32.store8
+ (get_local $i1)
+ (i32.load8_s
+ (get_local $i2)
+ )
+ )
+ (set_local $i1
+ (i32.add
+ (get_local $i1)
+ (i32.const 1)
+ )
+ )
+ (set_local $i2
+ (i32.add
+ (get_local $i2)
+ (i32.const 1)
+ )
+ )
+ (set_local $i3
+ (i32.sub
+ (get_local $i3)
+ (i32.const 1)
+ )
+ )
+ )
+ (br $while-in$1)
+ )
+ (loop $while-out$2 $while-in$3
+ (if
+ (i32.eqz
+ (i32.ge_s
+ (get_local $i3)
+ (i32.const 4)
+ )
+ )
+ (br $while-out$2)
+ )
+ (block
+ (i32.store
+ (get_local $i1)
+ (i32.load
+ (get_local $i2)
+ )
+ )
+ (set_local $i1
+ (i32.add
+ (get_local $i1)
+ (i32.const 4)
+ )
+ )
+ (set_local $i2
+ (i32.add
+ (get_local $i2)
+ (i32.const 4)
+ )
+ )
+ (set_local $i3
+ (i32.sub
+ (get_local $i3)
+ (i32.const 4)
+ )
+ )
+ )
+ (br $while-in$3)
+ )
+ )
+ )
+ (loop $while-out$4 $while-in$5
+ (if
+ (i32.eqz
+ (i32.gt_s
+ (get_local $i3)
+ (i32.const 0)
+ )
+ )
+ (br $while-out$4)
+ )
+ (block
+ (i32.store8
+ (get_local $i1)
+ (i32.load8_s
+ (get_local $i2)
+ )
+ )
+ (set_local $i1
+ (i32.add
+ (get_local $i1)
+ (i32.const 1)
+ )
+ )
+ (set_local $i2
+ (i32.add
+ (get_local $i2)
+ (i32.const 1)
+ )
+ )
+ (set_local $i3
+ (i32.sub
+ (get_local $i3)
+ (i32.const 1)
+ )
+ )
+ )
+ (br $while-in$5)
+ )
+ (return
+ (get_local $i4)
+ )
+ )
+ (func $this-is-effective-i-tell-you (param $x i32)
+ (if
+ (i32.const -1)
+ (block
+ (if ;; this is important for the bug
+ (i32.const 0)
+ (nop)
+ )
+ (set_local $x ;; this set is effective!
+ (i32.const 1)
+ )
+ )
+ (nop) ;; this is enough for the bug
+ )
+ (get_local $x) ;; this ends up with the wrong value in the test
+ )
+)
+