summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ir/LocalGraph.cpp7
-rw-r--r--src/ir/local-graph.h3
-rw-r--r--src/passes/CMakeLists.txt1
-rw-r--r--src/passes/MergeLocals.cpp220
-rw-r--r--src/passes/pass.cpp5
-rw-r--r--src/passes/passes.h1
-rw-r--r--src/tools/fuzzing.h14
-rw-r--r--src/tools/wasm-opt.cpp48
-rw-r--r--test/passes/merge-locals.txt460
-rw-r--r--test/passes/merge-locals.wast379
-rw-r--r--test/passes/translate-to-fuzz.txt1362
11 files changed, 2328 insertions, 172 deletions
diff --git a/src/ir/LocalGraph.cpp b/src/ir/LocalGraph.cpp
index cee187c6d..53c6a52e2 100644
--- a/src/ir/LocalGraph.cpp
+++ b/src/ir/LocalGraph.cpp
@@ -127,11 +127,8 @@ void LocalGraph::visitLoop(Loop* curr) {
// the get trivially has fewer sets, so it overrode the loop entry sets
return;
}
- std::vector<SetLocal*> intersection;
- std::set_intersection(beforeSets.begin(), beforeSets.end(),
- getSets.begin(), getSets.end(),
- std::back_inserter(intersection));
- if (intersection.size() < beforeSets.size()) {
+ if (!std::includes(getSets.begin(), getSets.end(),
+ beforeSets.begin(), beforeSets.end())) {
// the get has not the same sets as in the loop entry
return;
}
diff --git a/src/ir/local-graph.h b/src/ir/local-graph.h
index 4c4c1ee0a..ea27fa1fb 100644
--- a/src/ir/local-graph.h
+++ b/src/ir/local-graph.h
@@ -28,8 +28,7 @@ namespace wasm {
// on this).
//
// TODO: the algorithm here is pretty simple, but also pretty slow,
-// we should optimize it. e.g. we rely on set_interaction
-// here, and worse we only use it to compute the size...
+// we should optimize it.
struct LocalGraph : public PostWalker<LocalGraph> {
// main API
diff --git a/src/passes/CMakeLists.txt b/src/passes/CMakeLists.txt
index 168af2761..e9a157499 100644
--- a/src/passes/CMakeLists.txt
+++ b/src/passes/CMakeLists.txt
@@ -17,6 +17,7 @@ SET(passes_SOURCES
InstrumentMemory.cpp
MemoryPacking.cpp
MergeBlocks.cpp
+ MergeLocals.cpp
Metrics.cpp
NameList.cpp
OptimizeInstructions.cpp
diff --git a/src/passes/MergeLocals.cpp b/src/passes/MergeLocals.cpp
new file mode 100644
index 000000000..31be495c1
--- /dev/null
+++ b/src/passes/MergeLocals.cpp
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Merges locals when it is beneficial to do so.
+//
+// An obvious case is when locals are copied. In that case, two locals have the
+// same value in a range, and we can pick which of the two to use. For
+// example, in
+//
+// (if (result i32)
+// (tee_local $x
+// (get_local $y)
+// )
+// (i32.const 100)
+// (get_local $x)
+// )
+//
+// If that assignment of $y is never used again, everything is fine. But if
+// if is, then the live range of $y does not end in that get, and will
+// necessarily overlap with that of $x - making them appear to interfere
+// with each other in coalesce-locals, even though the value is identical.
+//
+// To fix that, we replace uses of $y with uses of $x. This extends $x's
+// live range and shrinks $y's live range. This tradeoff is not always good,
+// but $x and $y definitely overlap already, so trying to shrink the overlap
+// makes sense - if we remove the overlap entirely, we may be able to let
+// $x and $y be coalesced later.
+//
+// If we can remove only some of $y's uses, then we are definitely not
+// removing the overlap, and they do conflict. In that case, it's not clear
+// if this is beneficial or not, and we don't do it for now
+// TODO: investigate more
+//
+
+#include <wasm.h>
+#include <pass.h>
+#include <wasm-builder.h>
+#include <ir/local-graph.h>
+
+namespace wasm {
+
+struct MergeLocals : public WalkerPass<PostWalker<MergeLocals, UnifiedExpressionVisitor<MergeLocals>>> {
+ bool isFunctionParallel() override { return true; }
+
+ Pass* create() override { return new MergeLocals(); }
+
+ void doWalkFunction(Function* func) {
+ // first, instrument the graph by modifying each copy
+ // (set_local $x
+ // (get_local $y)
+ // )
+ // to
+ // (set_local $x
+ // (tee_local $y
+ // (get_local $y)
+ // )
+ // )
+ // That is, we add a trivial assign of $y. This ensures we
+ // have a new assignment of $y at the location of the copy,
+ // which makes it easy for us to see if the value if $y
+ // is still used after that point
+ super::doWalkFunction(func);
+
+ // optimize the copies, merging when we can, and removing
+ // the trivial assigns we added temporarily
+ optimizeCopies();
+ }
+
+ std::vector<SetLocal*> copies;
+
+ void visitSetLocal(SetLocal* curr) {
+ if (auto* get = curr->value->dynCast<GetLocal>()) {
+ if (get->index != curr->index) {
+ Builder builder(*getModule());
+ auto* trivial = builder.makeTeeLocal(get->index, get);
+ curr->value = trivial;
+ copies.push_back(curr);
+ }
+ }
+ }
+
+ void optimizeCopies() {
+ if (copies.empty()) return;
+ // compute all dependencies
+ LocalGraph preGraph(getFunction(), getModule());
+ preGraph.computeInfluences();
+ // optimize each copy
+ std::unordered_map<SetLocal*, SetLocal*> optimizedToCopy, optimizedToTrivial;
+ for (auto* copy : copies) {
+ auto* trivial = copy->value->cast<SetLocal>();
+ bool canOptimizeToCopy = false;
+ auto& trivialInfluences = preGraph.setInfluences[trivial];
+ if (!trivialInfluences.empty()) {
+ canOptimizeToCopy = true;
+ for (auto* influencedGet : trivialInfluences) {
+ // this get uses the trivial write, so it uses the value in the copy.
+ // however, it may depend on other writes too, if there is a merge/phi,
+ // and in that case we can't do anything
+ assert(influencedGet->index == trivial->index);
+ if (preGraph.getSetses[influencedGet].size() == 1) {
+ // this is ok
+ assert(*preGraph.getSetses[influencedGet].begin() == trivial);
+ } else {
+ canOptimizeToCopy = false;
+ break;
+ }
+ }
+ }
+ if (canOptimizeToCopy) {
+ // worth it for this copy, do it
+ for (auto* influencedGet : trivialInfluences) {
+ influencedGet->index = copy->index;
+ }
+ optimizedToCopy[copy] = trivial;
+ } else {
+ // alternatively, we can try to remove the conflict in the opposite way: given
+ // (set_local $x
+ // (get_local $y)
+ // )
+ // we can look for uses of $x that could instead be uses of $y. this extends
+ // $y's live range, but if it removes the conflict between $x and $y, it may be
+ // worth it
+ if (!trivialInfluences.empty()) { // if the trivial set we added has influences, it means $y lives on
+ auto& copyInfluences = preGraph.setInfluences[copy];
+ if (!copyInfluences.empty()) {
+ bool canOptimizeToTrivial = true;
+ for (auto* influencedGet : copyInfluences) {
+ // as above, avoid merges/phis
+ assert(influencedGet->index == copy->index);
+ if (preGraph.getSetses[influencedGet].size() == 1) {
+ // this is ok
+ assert(*preGraph.getSetses[influencedGet].begin() == copy);
+ } else {
+ canOptimizeToTrivial = false;
+ break;
+ }
+ }
+ if (canOptimizeToTrivial) {
+ // worth it for this copy, do it
+ for (auto* influencedGet : copyInfluences) {
+ influencedGet->index = trivial->index;
+ }
+ optimizedToTrivial[copy] = trivial;
+ // note that we don't
+ }
+ }
+ }
+ }
+ }
+ if (!optimizedToCopy.empty() || !optimizedToTrivial.empty()) {
+ // finally, we need to verify that the changes work properly, that is,
+ // they use the value from the right place (and are not affected by
+ // another set of the index we changed to).
+ // 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());
+ postGraph.computeInfluences();
+ for (auto& pair : optimizedToCopy) {
+ auto* copy = pair.first;
+ auto* trivial = pair.second;
+ auto& trivialInfluences = preGraph.setInfluences[trivial];
+ for (auto* influencedGet : trivialInfluences) {
+ // verify the set
+ auto& sets = postGraph.getSetses[influencedGet];
+ if (sets.size() != 1 || *sets.begin() != copy) {
+ // not good, undo all the changes for this copy
+ for (auto* undo : trivialInfluences) {
+ undo->index = trivial->index;
+ }
+ break;
+ }
+ }
+ }
+ for (auto& pair : optimizedToTrivial) {
+ auto* copy = pair.first;
+ auto* trivial = pair.second;
+ auto& copyInfluences = preGraph.setInfluences[copy];
+ for (auto* influencedGet : copyInfluences) {
+ // verify the set
+ auto& sets = postGraph.getSetses[influencedGet];
+ if (sets.size() != 1 || *sets.begin() != trivial) {
+ // not good, undo all the changes for this copy
+ for (auto* undo : copyInfluences) {
+ undo->index = copy->index;
+ }
+ break;
+ }
+ }
+ // if this change was ok, we can probably remove the copy itself,
+ // but we leave that for other passes
+ }
+ }
+ // remove the trivial sets
+ for (auto* copy : copies) {
+ copy->value = copy->value->cast<SetLocal>()->value;
+ }
+ }
+};
+
+Pass *createMergeLocalsPass() {
+ return new MergeLocals();
+}
+
+} // namespace wasm
+
diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp
index f2463691a..89467899c 100644
--- a/src/passes/pass.cpp
+++ b/src/passes/pass.cpp
@@ -83,6 +83,7 @@ void PassRegistry::registerPasses() {
registerPass("instrument-memory", "instrument the build with code to intercept all loads and stores", createInstrumentMemoryPass);
registerPass("memory-packing", "packs memory into separate segments, skipping zeros", createMemoryPackingPass);
registerPass("merge-blocks", "merges blocks to their parents", createMergeBlocksPass);
+ registerPass("merge-locals", "merges locals when beneficial", createMergeLocalsPass);
registerPass("metrics", "reports metrics", createMetricsPass);
registerPass("nm", "name list", createNameListPass);
registerPass("optimize-instructions", "optimizes instruction combinations", createOptimizeInstructionsPass);
@@ -140,6 +141,10 @@ void PassRunner::addDefaultFunctionOptimizationPasses() {
add("vacuum"); // previous pass creates garbage
add("reorder-locals");
add("remove-unused-brs"); // simplify-locals opens opportunities for optimizations
+ // if we are willing to work hard, also optimize copies before coalescing
+ if (options.optimizeLevel >= 3 || options.shrinkLevel >= 2) {
+ add("merge-locals"); // very slow on e.g. sqlite
+ }
add("coalesce-locals");
add("simplify-locals");
add("vacuum"); // previous pass creates garbage
diff --git a/src/passes/passes.h b/src/passes/passes.h
index 957ca2d68..5cefdea17 100644
--- a/src/passes/passes.h
+++ b/src/passes/passes.h
@@ -42,6 +42,7 @@ Pass* createInstrumentLocalsPass();
Pass* createInstrumentMemoryPass();
Pass* createMemoryPackingPass();
Pass* createMergeBlocksPass();
+Pass* createMergeLocalsPass();
Pass* createMinifiedPrinterPass();
Pass* createMetricsPass();
Pass* createNameListPass();
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h
index be12b561a..baaaf1b23 100644
--- a/src/tools/fuzzing.h
+++ b/src/tools/fuzzing.h
@@ -480,6 +480,14 @@ private:
}
Expression* _makeConcrete(WasmType type) {
+ auto choice = upTo(100);
+ if (choice < 10) return makeConst(type);
+ if (choice < 30) return makeSetLocal(type);
+ if (choice < 50) return makeGetLocal(type);
+ if (choice < 60) return makeBlock(type);
+ if (choice < 70) return makeIf(type);
+ if (choice < 80) return makeLoop(type);
+ if (choice < 90) return makeBreak(type);
switch (upTo(15)) {
case 0: return makeBlock(type);
case 1: return makeIf(type);
@@ -501,6 +509,12 @@ private:
}
Expression* _makenone() {
+ auto choice = upTo(100);
+ if (choice < 50) return makeSetLocal(none);
+ if (choice < 60) return makeBlock(none);
+ if (choice < 70) return makeIf(none);
+ if (choice < 80) return makeLoop(none);
+ if (choice < 90) return makeBreak(none);
switch (upTo(11)) {
case 0: return makeBlock(none);
case 1: return makeIf(none);
diff --git a/src/tools/wasm-opt.cpp b/src/tools/wasm-opt.cpp
index 8fe55aef1..374c1b5bd 100644
--- a/src/tools/wasm-opt.cpp
+++ b/src/tools/wasm-opt.cpp
@@ -182,36 +182,38 @@ int main(int argc, const char* argv[]) {
std::cout << "[extra-fuzz-command first output:]\n" << firstOutput << '\n';
}
+ Module* curr = &wasm;
+ Module other;
+
+ if (fuzzExec && fuzzBinary) {
+ BufferWithRandomAccess buffer(false);
+ // write the binary
+ WasmBinaryWriter writer(&wasm, buffer, false);
+ writer.write();
+ // read the binary
+ auto input = buffer.getAsChars();
+ WasmBinaryBuilder parser(other, input, false);
+ parser.read();
+ bool valid = WasmValidator().validate(other, features);
+ if (!valid) {
+ WasmPrinter::printModule(&other);
+ }
+ assert(valid);
+ curr = &other;
+ }
+
if (options.runningPasses()) {
if (options.debug) std::cerr << "running passes...\n";
- options.runPasses(wasm);
- bool valid = WasmValidator().validate(wasm, features);
+ options.runPasses(*curr);
+ bool valid = WasmValidator().validate(*curr, features);
if (!valid) {
- WasmPrinter::printModule(&wasm);
+ WasmPrinter::printModule(&*curr);
}
assert(valid);
}
if (fuzzExec) {
- auto* compare = &wasm;
- Module second;
- if (fuzzBinary) {
- compare = &second;
- BufferWithRandomAccess buffer(false);
- // write the binary
- WasmBinaryWriter writer(&wasm, buffer, false);
- writer.write();
- // read the binary
- auto input = buffer.getAsChars();
- WasmBinaryBuilder parser(second, input, false);
- parser.read();
- bool valid = WasmValidator().validate(second, features);
- if (!valid) {
- WasmPrinter::printModule(&second);
- }
- assert(valid);
- }
- results.check(*compare);
+ results.check(*curr);
}
if (options.extra.count("output") > 0) {
@@ -220,7 +222,7 @@ int main(int argc, const char* argv[]) {
writer.setDebug(options.debug);
writer.setBinary(emitBinary);
writer.setDebugInfo(debugInfo);
- writer.write(wasm, options.extra["output"]);
+ writer.write(*curr, options.extra["output"]);
if (extraFuzzCommand.size() > 0) {
auto secondOutput = runCommand(extraFuzzCommand);
diff --git a/test/passes/merge-locals.txt b/test/passes/merge-locals.txt
new file mode 100644
index 000000000..21a71af4c
--- /dev/null
+++ b/test/passes/merge-locals.txt
@@ -0,0 +1,460 @@
+(module
+ (type $0 (func (param i32 i32) (result i32)))
+ (type $1 (func (param i32 i32)))
+ (type $2 (func (param i32 f32 f32) (result i64)))
+ (type $3 (func (param i32 i32 i32)))
+ (global $global$0 (mut i32) (i32.const 10))
+ (memory $0 0)
+ (func $test (; 0 ;) (type $0) (param $x i32) (param $y i32) (result i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (get_local $x)
+ )
+ )
+ (get_local $x)
+ )
+ (func $test2 (; 1 ;) (type $0) (param $x i32) (param $y i32) (result i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (get_local $x)
+ )
+ )
+ (get_local $x)
+ )
+ (func $test-multiple (; 2 ;) (type $0) (param $x i32) (param $y i32) (result i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (get_local $x)
+ )
+ )
+ (drop
+ (get_local $x)
+ )
+ (get_local $x)
+ )
+ (func $test-just-some (; 3 ;) (type $0) (param $x i32) (param $y i32) (result i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (get_local $x)
+ )
+ )
+ (drop
+ (get_local $x)
+ )
+ (set_local $y
+ (i32.const 200)
+ )
+ (get_local $y)
+ )
+ (func $test-just-some2 (; 4 ;) (type $0) (param $x i32) (param $y i32) (result i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (get_local $x)
+ )
+ )
+ (if
+ (i32.const 300)
+ (set_local $y
+ (i32.const 400)
+ )
+ (drop
+ (get_local $x)
+ )
+ )
+ (i32.const 500)
+ )
+ (func $test-just-some3 (; 5 ;) (type $0) (param $x i32) (param $y i32) (result i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (i32.const 200)
+ )
+ )
+ (if
+ (i32.const 300)
+ (set_local $y
+ (i32.const 400)
+ )
+ (drop
+ (get_local $y)
+ )
+ )
+ (get_local $y)
+ )
+ (func $silly-self (; 6 ;) (type $0) (param $x i32) (param $y i32) (result i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $x)
+ )
+ (i32.const 100)
+ (get_local $x)
+ )
+ )
+ (get_local $y)
+ )
+ (func $silly-multi (; 7 ;) (type $0) (param $x i32) (param $y i32) (result i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (tee_local $y
+ (get_local $x)
+ )
+ )
+ (i32.const 100)
+ (get_local $x)
+ )
+ )
+ (get_local $y)
+ )
+ (func $undo-1 (; 8 ;) (type $1) (param $var$1 i32) (param $var$2 i32)
+ (local $var$5 i32)
+ (set_local $var$2
+ (get_local $var$1)
+ )
+ (set_local $var$2
+ (i32.const 1)
+ )
+ (drop
+ (get_local $var$1)
+ )
+ )
+ (func $undo-2 (; 9 ;) (type $1) (param $var$1 i32) (param $var$2 i32)
+ (local $var$5 i32)
+ (set_local $var$2
+ (get_local $var$1)
+ )
+ (if
+ (get_local $var$1)
+ (set_local $var$2
+ (i32.const 1)
+ )
+ )
+ (drop
+ (get_local $var$1)
+ )
+ )
+ (func $reverse (; 10 ;) (type $1) (param $x i32) (param $y i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (get_local $y)
+ )
+ )
+ (drop
+ (get_local $y)
+ )
+ (if
+ (i32.const 1)
+ (set_local $y
+ (i32.const 200)
+ )
+ )
+ (drop
+ (get_local $y)
+ )
+ )
+ (func $reverse-end (; 11 ;) (type $1) (param $x i32) (param $y i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (get_local $x)
+ )
+ )
+ )
+ (func $reverse-lone-end-2 (; 12 ;) (type $1) (param $x i32) (param $y i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (get_local $x)
+ )
+ )
+ (set_local $y
+ (i32.const 200)
+ )
+ (drop
+ (get_local $y)
+ )
+ )
+ (func $reverse-undo (; 13 ;) (type $1) (param $x i32) (param $y i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (get_local $y)
+ )
+ )
+ (set_local $x
+ (i32.const 300)
+ )
+ (drop
+ (get_local $x)
+ )
+ (if
+ (i32.const 1)
+ (set_local $y
+ (i32.const 200)
+ )
+ )
+ (drop
+ (get_local $y)
+ )
+ )
+ (func $reverse-undo2 (; 14 ;) (type $1) (param $x i32) (param $y i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (i32.const 150)
+ )
+ )
+ (set_local $x
+ (i32.const 300)
+ )
+ (drop
+ (get_local $x)
+ )
+ (if
+ (i32.const 1)
+ (set_local $y
+ (i32.const 200)
+ )
+ )
+ (drop
+ (get_local $y)
+ )
+ )
+ (func $reverse-undo3-conditional (; 15 ;) (type $1) (param $x i32) (param $y i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (i32.const 150)
+ )
+ )
+ (if
+ (i32.const 1)
+ (set_local $x
+ (i32.const 300)
+ )
+ )
+ (drop
+ (get_local $x)
+ )
+ (if
+ (i32.const 1)
+ (set_local $y
+ (i32.const 200)
+ )
+ )
+ (drop
+ (get_local $y)
+ )
+ )
+ (func $reverse-undo3-conditional-b (; 16 ;) (type $1) (param $x i32) (param $y i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (get_local $x)
+ )
+ )
+ (if
+ (i32.const 1)
+ (set_local $x
+ (i32.const 300)
+ )
+ )
+ (drop
+ (get_local $x)
+ )
+ (if
+ (i32.const 1)
+ (set_local $y
+ (i32.const 200)
+ )
+ )
+ (drop
+ (get_local $y)
+ )
+ )
+ (func $reverse-undo3-conditional-c (; 17 ;) (type $1) (param $x i32) (param $y i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (i32.const 150)
+ )
+ )
+ (if
+ (i32.const 1)
+ (drop
+ (get_local $x)
+ )
+ (block $block
+ (if
+ (i32.const 1)
+ (set_local $x
+ (i32.const 300)
+ )
+ )
+ (drop
+ (get_local $x)
+ )
+ )
+ )
+ (if
+ (i32.const 1)
+ (set_local $y
+ (i32.const 200)
+ )
+ )
+ (drop
+ (get_local $y)
+ )
+ )
+ (func $fuzz (; 18 ;) (type $2) (param $var$0 i32) (param $var$1 f32) (param $var$2 f32) (result i64)
+ (local $var$3 i32)
+ (set_global $global$0
+ (i32.sub
+ (get_global $global$0)
+ (i32.const 1)
+ )
+ )
+ (loop $label$1 (result i64)
+ (set_global $global$0
+ (i32.sub
+ (get_global $global$0)
+ (i32.const 1)
+ )
+ )
+ (br_if $label$1
+ (block $label$2 (result i32)
+ (drop
+ (if (result i32)
+ (block $label$3 (result i32)
+ (set_global $global$0
+ (i32.sub
+ (get_global $global$0)
+ (i32.const 3)
+ )
+ )
+ (set_local $var$3
+ (i32.const 1)
+ )
+ (tee_local $var$3
+ (get_local $var$0)
+ )
+ )
+ (i32.const 0)
+ (block $block (result i32)
+ (set_local $var$3
+ (if (result i32)
+ (i32.const 0)
+ (block $block13 (result i32)
+ (block $label$7
+ (block $label$8
+ (set_local $var$0
+ (i32.const 34738786)
+ )
+ )
+ )
+ (get_local $var$3)
+ )
+ (block $block14 (result i32)
+ (if
+ (i32.eqz
+ (get_global $global$0)
+ )
+ (return
+ (i64.const 137438953472)
+ )
+ )
+ (set_global $global$0
+ (i32.sub
+ (get_global $global$0)
+ (i32.const 1)
+ )
+ )
+ (br_if $label$1
+ (i32.eqz
+ (get_local $var$3)
+ )
+ )
+ (return
+ (i64.const 44125)
+ )
+ )
+ )
+ )
+ (i32.const -129)
+ )
+ )
+ )
+ (i32.const 0)
+ )
+ )
+ (i64.const -36028797018963968)
+ )
+ )
+ (func $trivial-confusion (; 19 ;) (type $3) (param $unused i32) (param $param i32) (param $result i32)
+ (loop $label$1
+ (if
+ (i32.const 1)
+ (drop
+ (get_local $result)
+ )
+ )
+ (set_local $result
+ (get_local $param)
+ )
+ (br_if $label$1
+ (tee_local $unused
+ (get_local $param)
+ )
+ )
+ )
+ )
+)
diff --git a/test/passes/merge-locals.wast b/test/passes/merge-locals.wast
new file mode 100644
index 000000000..1eb979c4c
--- /dev/null
+++ b/test/passes/merge-locals.wast
@@ -0,0 +1,379 @@
+(module
+ (global $global$0 (mut i32) (i32.const 10))
+ (func $test (param $x $i32) (param $y i32) (result i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (get_local $x)
+ )
+ )
+ (get_local $y) ;; turn this into $x
+ )
+ (func $test2 (param $x $i32) (param $y i32) (result i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (get_local $x)
+ )
+ )
+ (get_local $x)
+ )
+ (func $test-multiple (param $x $i32) (param $y i32) (result i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (get_local $x)
+ )
+ )
+ (drop (get_local $y)) ;; turn this into $x
+ (get_local $y) ;; turn this into $x
+ )
+ (func $test-just-some (param $x $i32) (param $y i32) (result i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (get_local $x)
+ )
+ )
+ (drop (get_local $y)) ;; turn this into $x
+ (set_local $y (i32.const 200))
+ (get_local $y) ;; but not this one!
+ )
+ (func $test-just-some2 (param $x $i32) (param $y i32) (result i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (get_local $x)
+ )
+ )
+ (if
+ (i32.const 300)
+ (set_local $y (i32.const 400))
+ (drop (get_local $y)) ;; turn this into $x
+ )
+ (i32.const 500)
+ )
+ (func $test-just-some3 (param $x $i32) (param $y i32) (result i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (i32.const 200)
+ )
+ )
+ (if
+ (i32.const 300)
+ (set_local $y (i32.const 400))
+ (drop (get_local $y)) ;; can turn this into $x, but another exists we can't, so do nothing
+ )
+ (get_local $y) ;; but not this one!
+ )
+ (func $silly-self (param $x $i32) (param $y i32) (result i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $x)
+ )
+ (i32.const 100)
+ (get_local $x)
+ )
+ )
+ (get_local $y) ;; turn this into $x
+ )
+ (func $silly-multi (param $x $i32) (param $y i32) (result i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (tee_local $y
+ (get_local $x)
+ )
+ )
+ (i32.const 100)
+ (get_local $x)
+ )
+ )
+ (get_local $y) ;; turn this into $x
+ )
+ (func $undo-1 (param $var$1 i32) (param $var$2 i32)
+ (local $var$5 i32)
+ (set_local $var$2 ;; copy 1 to 2
+ (get_local $var$1)
+ )
+ (set_local $var$2 ;; overwrite 2
+ (i32.const 1)
+ )
+ (drop
+ (get_local $var$1) ;; can't be changed to $var$2, as it changes
+ )
+ )
+ (func $undo-2 (param $var$1 i32) (param $var$2 i32)
+ (local $var$5 i32)
+ (set_local $var$2 ;; copy 1 to 2
+ (get_local $var$1)
+ )
+ (if (get_local $var$1)
+ (set_local $var$2 ;; conditional overwrite 2
+ (i32.const 1)
+ )
+ )
+ (drop
+ (get_local $var$1) ;; can't be changed to $var$2, as it changes
+ )
+ )
+ (func $reverse (param $x $i32) (param $y i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (get_local $x)
+ )
+ )
+ (drop (get_local $x)) ;; (read lower down first) but the reverse can work!
+ (if (i32.const 1)
+ (set_local $y (i32.const 200))
+ )
+ (drop (get_local $y)) ;; cannot this into $x, since this $y has multiple sources
+ )
+ (func $reverse-end (param $x $i32) (param $y i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (get_local $x) ;; don't change to $y, as its lifetime ended. leave it ended
+ )
+ )
+ )
+ (func $reverse-lone-end-2 (param $x $i32) (param $y i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (get_local $x) ;; don't change to $y, as its lifetime ended. leave it ended
+ )
+ )
+ (set_local $y (i32.const 200))
+ (drop (get_local $y)) ;; cannot this into $x, since this $y has multiple sources
+ )
+ (func $reverse-undo (param $x $i32) (param $y i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (get_local $x) ;; can optimize this ($y lives on)
+ )
+ )
+ (set_local $x (i32.const 300)) ;; force an undo
+ (drop (get_local $x)) ;; (read lower down first) but the reverse can almost work
+ (if (i32.const 1)
+ (set_local $y (i32.const 200))
+ )
+ (drop (get_local $y)) ;; cannot this into $x, since this $y has multiple sources
+ )
+ (func $reverse-undo2 (param $x $i32) (param $y i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (i32.const 150)
+ )
+ )
+ (set_local $x (i32.const 300)) ;; force an undo
+ (drop (get_local $x)) ;; (read lower down first) but the reverse can almost work
+ (if (i32.const 1)
+ (set_local $y (i32.const 200))
+ )
+ (drop (get_local $y)) ;; cannot this into $x, since this $y has multiple sources
+ )
+ (func $reverse-undo3-conditional (param $x $i32) (param $y i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (i32.const 150)
+ )
+ )
+ (if (i32.const 1)
+ (set_local $x (i32.const 300)) ;; force an undo
+ )
+ (drop (get_local $x)) ;; (read lower down first) but the reverse can almost work
+ (if (i32.const 1)
+ (set_local $y (i32.const 200))
+ )
+ (drop (get_local $y)) ;; cannot this into $x, since this $y has multiple sources
+ )
+ (func $reverse-undo3-conditional-b (param $x $i32) (param $y i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (get_local $x)
+ )
+ )
+ (if (i32.const 1)
+ (set_local $x (i32.const 300)) ;; force an undo
+ )
+ (drop (get_local $x)) ;; (read lower down first) but the reverse can almost work
+ (if (i32.const 1)
+ (set_local $y (i32.const 200))
+ )
+ (drop (get_local $y)) ;; cannot this into $x, since this $y has multiple sources
+ )
+ (func $reverse-undo3-conditional-c (param $x $i32) (param $y i32)
+ (drop
+ (if (result i32)
+ (tee_local $x
+ (get_local $y)
+ )
+ (i32.const 100)
+ (i32.const 150)
+ )
+ )
+ (if (i32.const 1)
+ (drop (get_local $x))
+ (block
+ (if (i32.const 1)
+ (set_local $x (i32.const 300)) ;; force an undo
+ )
+ (drop (get_local $x)) ;; (read lower down first) but the reverse can almost work
+ )
+ )
+ (if (i32.const 1)
+ (set_local $y (i32.const 200))
+ )
+ (drop (get_local $y)) ;; cannot this into $x, since this $y has multiple sources
+ )
+ (func $fuzz (param $var$0 i32) (param $var$1 f32) (param $var$2 f32) (result i64)
+ (local $var$3 i32)
+ (set_global $global$0
+ (i32.sub
+ (get_global $global$0)
+ (i32.const 1)
+ )
+ )
+ (loop $label$1 (result i64)
+ (set_global $global$0
+ (i32.sub
+ (get_global $global$0)
+ (i32.const 1)
+ )
+ )
+ (br_if $label$1
+ (block $label$2 (result i32)
+ (drop
+ (if (result i32)
+ (block $label$3 (result i32)
+ (set_global $global$0
+ (i32.sub
+ (get_global $global$0)
+ (i32.const 3)
+ )
+ )
+ (set_local $var$3
+ (i32.const 1)
+ )
+ (tee_local $var$3
+ (get_local $var$0)
+ )
+ )
+ (i32.const 0)
+ (block (result i32)
+ (set_local $var$3
+ (if (result i32)
+ (i32.const 0)
+ (block (result i32)
+ (block $label$7
+ (block $label$8
+ (set_local $var$0
+ (i32.const 34738786)
+ )
+ )
+ )
+ (get_local $var$3)
+ )
+ (block (result i32)
+ (if
+ (i32.eqz
+ (get_global $global$0)
+ )
+ (return
+ (i64.const 137438953472)
+ )
+ )
+ (set_global $global$0
+ (i32.sub
+ (get_global $global$0)
+ (i32.const 1)
+ )
+ )
+ (br_if $label$1
+ (i32.eqz
+ (get_local $var$3)
+ )
+ )
+ (return
+ (i64.const 44125)
+ )
+ )
+ )
+ )
+ (i32.const -129)
+ )
+ )
+ )
+ (i32.const 0)
+ )
+ )
+ (i64.const -36028797018963968)
+ )
+ )
+ (func $trivial-confusion (param $unused i32) (param $param i32) (param $result i32)
+ (loop $label$1
+ (if
+ (i32.const 1)
+ (drop
+ (get_local $result)
+ )
+ )
+ (set_local $result ;; vanishes
+ (get_local $param)
+ )
+ (br_if $label$1
+ (tee_local $unused ;; unused, but forms part of a copy, with $result - the trivial tee we add here should not confuse us
+ (get_local $result) ;; flips
+ )
+ )
+ )
+ )
+)
+
diff --git a/test/passes/translate-to-fuzz.txt b/test/passes/translate-to-fuzz.txt
index 09ea57017..0bcb215f6 100644
--- a/test/passes/translate-to-fuzz.txt
+++ b/test/passes/translate-to-fuzz.txt
@@ -1,27 +1,25 @@
(module
(type $FUNCSIG$i (func (result i32)))
(type $FUNCSIG$v (func))
- (type $FUNCSIG$ffff (func (param f32 f32 f32) (result f32)))
- (type $FUNCSIG$f (func (result f32)))
- (type $FUNCSIG$jif (func (param i32 f32) (result i64)))
+ (type $FUNCSIG$j (func (result i64)))
+ (type $FUNCSIG$jddi (func (param f64 f64 i32) (result i64)))
(global $global$0 (mut f32) (f32.const 536870912))
(global $global$1 (mut f32) (f32.const 2147483648))
(global $global$2 (mut f64) (f64.const -1048576))
(global $global$3 (mut f64) (f64.const 23643))
(global $hangLimit (mut i32) (i32.const 10))
- (table 2 2 anyfunc)
- (elem (i32.const 0) $func_6 $func_6)
+ (table 8 anyfunc)
+ (elem (i32.const 0) $func_0 $func_0 $func_0 $func_0 $func_4 $func_4 $func_5 $func_8)
(memory $0 (shared 1 1))
(data (i32.const 0) "n\00\05E\00\00\00\00")
(export "func_0" (func $func_0))
(export "func_1" (func $func_1))
- (export "func_1_invoker" (func $func_1_invoker))
- (export "func_3_invoker" (func $func_3_invoker))
- (export "func_5" (func $func_5))
+ (export "func_2" (func $func_2))
+ (export "func_2_invoker" (func $func_2_invoker))
+ (export "func_4" (func $func_4))
(export "func_6" (func $func_6))
(export "func_6_invoker" (func $func_6_invoker))
- (export "func_8" (func $func_8))
- (export "func_8_invoker" (func $func_8_invoker))
+ (export "func_9_invoker" (func $func_9_invoker))
(export "hangLimitInitializer" (func $hangLimitInitializer))
(func $func_0 (; 0 ;) (type $FUNCSIG$i) (result i32)
(local $0 i32)
@@ -34,7 +32,7 @@
(get_global $hangLimit)
)
(return
- (get_local $0)
+ (i32.const 1159202307)
)
)
(set_global $hangLimit
@@ -45,56 +43,258 @@
)
)
(block $label$0 (result i32)
- (set_local $1
- (if (result f64)
- (i32.const -2147483648)
- (call $deNan64
- (f64.add
- (call $deNan64
- (f64.promote/f32
- (f32.const 4.212240583647452e-17)
+ (if
+ (tee_local $0
+ (i32.const 1577934664)
+ )
+ (block $label$1
+ (set_local $1
+ (f64.const 9223372036854775808)
+ )
+ (loop $label$2
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (get_local $0)
)
)
- (block $label$14 (result f64)
- (block $label$15
- (drop
- (if (result f64)
- (i32.eqz
- (i32.const -32768)
- )
- (f64.load offset=22 align=4
- (i32.and
- (i32.popcnt
- (i32.const -65536)
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block $label$3
+ (set_local $0
+ (block $label$4 (result i32)
+ (set_local $0
+ (br_if $label$0
+ (i32.const 20063)
+ (if (result i32)
+ (i32.eqz
+ (br_if $label$4
+ (br_if $label$4
+ (get_local $0)
+ (i32.eqz
+ (if (result i32)
+ (i32.eqz
+ (if (result i32)
+ (get_local $0)
+ (i32.wrap/i64
+ (tee_local $3
+ (get_local $3)
+ )
+ )
+ (get_local $0)
+ )
+ )
+ (block $label$6 (result i32)
+ (nop)
+ (i32.const -127)
+ )
+ (block $label$7 (result i32)
+ (set_local $0
+ (i32.const -65535)
+ )
+ (loop $label$8 (result i32)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (i32.const 1696270109)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
+ (loop $label$5 (result i32)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (i32.const 1499815233)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block (result i32)
+ (set_local $1
+ (tee_local $1
+ (f64.const -nan:0xfffffffffffb5)
+ )
+ )
+ (br_if $label$5
+ (i32.eqz
+ (tee_local $0
+ (tee_local $0
+ (get_local $0)
+ )
+ )
+ )
+ )
+ (tee_local $0
+ (get_local $0)
+ )
+ )
+ )
)
- (i32.const 15)
)
- )
- (block $label$16 (result f64)
- (set_global $global$3
- (get_global $global$3)
+ (loop $label$9 (result i32)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (i32.const -2048)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block (result i32)
+ (nop)
+ (br_if $label$9
+ (i32.eqz
+ (block $label$10 (result i32)
+ (nop)
+ (loop $label$11 (result i32)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (get_local $0)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block (result i32)
+ (block $label$12
+ (block $label$13
+ (nop)
+ (set_local $2
+ (get_local $2)
+ )
+ )
+ (set_local $2
+ (get_local $2)
+ )
+ )
+ (br_if $label$11
+ (i32.const -1024)
+ )
+ (i32.const 65536)
+ )
+ )
+ )
+ )
+ )
+ (tee_local $0
+ (tee_local $0
+ (tee_local $0
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
+ (tee_local $0
+ (i32.const 2004515908)
)
- (br $label$15)
)
)
)
- (nop)
+ (br $label$1)
)
- (return
- (i32.const 28)
+ )
+ (br_if $label$1
+ (i32.eqz
+ (get_local $0)
)
)
)
)
- (f64.const 10)
)
+ (set_local $1
+ (get_local $1)
+ )
+ )
+ (if (result i32)
+ (i32.eqz
+ (get_local $0)
+ )
+ (tee_local $0
+ (i32.const 127)
+ )
+ (get_local $0)
+ )
+ )
+ )
+ (func $func_1 (; 1 ;) (type $FUNCSIG$i) (result i32)
+ (local $0 f64)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (i32.const -94)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block $label$0 (result i32)
+ (nop)
+ (set_local $0
+ (f64.const -3402823466385288598117041e14)
)
(return
- (i32.const 2151)
+ (i32.const -105)
)
)
)
- (func $func_1 (; 1 ;) (type $FUNCSIG$v)
+ (func $func_2 (; 2 ;) (type $FUNCSIG$v)
+ (local $0 f32)
+ (local $1 i64)
(block
(if
(i32.eqz
@@ -110,58 +310,54 @@
)
)
(block $label$0
- (nop)
- (loop $label$1
- (block
- (if
- (i32.eqz
- (get_global $hangLimit)
+ (set_local $0
+ (tee_local $0
+ (loop $label$1 (result f32)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return)
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
)
- (return)
- )
- (set_global $hangLimit
- (i32.sub
- (get_global $hangLimit)
- (i32.const 1)
+ (tee_local $0
+ (tee_local $0
+ (tee_local $0
+ (get_local $0)
+ )
+ )
)
)
)
- (block
- (block $label$2
- (nop)
- (nop)
- )
- (br_if $label$1
- (i32.eqz
- (i32.const 1079071296)
- )
+ )
+ (set_local $0
+ (tee_local $0
+ (tee_local $0
+ (get_local $0)
)
- (nop)
)
)
+ (nop)
)
)
- (func $func_1_invoker (; 2 ;) (type $FUNCSIG$v)
- (call $func_1)
- (call $func_1)
- (call $func_1)
- (call $func_1)
- (call $func_1)
- (call $func_1)
+ (func $func_2_invoker (; 3 ;) (type $FUNCSIG$v)
+ (call $func_2)
)
- (func $func_3 (; 3 ;) (result i32)
- (local $0 i32)
- (local $1 f64)
- (local $2 f64)
- (local $3 i32)
- (local $4 f32)
+ (func $func_4 (; 4 ;) (type $FUNCSIG$j) (result i64)
(block
(if
(i32.eqz
(get_global $hangLimit)
)
(return
- (i32.const -134217728)
+ (i64.const -55)
)
)
(set_global $hangLimit
@@ -171,30 +367,16 @@
)
)
)
- (unreachable)
+ (i64.const -2147483647)
)
- (func $func_3_invoker (; 4 ;) (type $FUNCSIG$v)
- (drop
- (call $func_3)
- )
- (drop
- (call $func_3)
- )
- (drop
- (call $func_3)
- )
- (drop
- (call $func_3)
- )
- )
- (func $func_5 (; 5 ;) (type $FUNCSIG$ffff) (param $0 f32) (param $1 f32) (param $2 f32) (result f32)
+ (func $func_5 (; 5 ;) (param $0 i64) (param $1 i64) (param $2 i32) (result f32)
(block
(if
(i32.eqz
(get_global $hangLimit)
)
(return
- (f32.const -1125899906842624)
+ (f32.const 6.635207710723736e-16)
)
)
(set_global $hangLimit
@@ -204,23 +386,505 @@
)
)
)
- (block $label$0
- (nop)
+ (block $label$0 (result f32)
+ (loop $label$1
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (f32.const 34359738368)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block $label$2
+ (block $label$3
+ (block $label$4
+ (loop $label$5
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (f32.const 77)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block
+ (set_local $1
+ (loop $label$6 (result i64)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (f32.const 0.05428441986441612)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block (result i64)
+ (block $label$7
+ (set_local $2
+ (loop $label$8 (result i32)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (f32.const -83)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block $label$9 (result i32)
+ (return
+ (f32.const 142.02503967285156)
+ )
+ )
+ )
+ )
+ (nop)
+ )
+ (br_if $label$6
+ (i32.const 127)
+ )
+ (tee_local $1
+ (tee_local $0
+ (get_local $1)
+ )
+ )
+ )
+ )
+ )
+ (br_if $label$5
+ (loop $label$10 (result i32)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (f32.const 891825984)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block (result i32)
+ (block $label$11
+ (nop)
+ (set_local $0
+ (loop $label$12 (result i64)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (f32.const -1099511627776)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block $label$13 (result i64)
+ (nop)
+ (i64.const 0)
+ )
+ )
+ )
+ )
+ (br_if $label$10
+ (i32.eqz
+ (get_local $2)
+ )
+ )
+ (get_local $2)
+ )
+ )
+ )
+ (if
+ (i32.eqz
+ (loop $label$14 (result i32)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (f32.const -2147483648)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block $label$15 (result i32)
+ (nop)
+ (i32.const -2147483648)
+ )
+ )
+ )
+ (block $label$16
+ (br_if $label$5
+ (i32.eqz
+ (loop $label$17 (result i32)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (f32.const 20584)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block (result i32)
+ (block $label$18
+ (nop)
+ (set_local $0
+ (tee_local $1
+ (block $label$19 (result i64)
+ (set_local $0
+ (tee_local $1
+ (tee_local $0
+ (i64.const 1807380624262974543)
+ )
+ )
+ )
+ (loop $label$20 (result i64)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (f32.const 9.902632491787244e-09)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block (result i64)
+ (loop $label$21
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (f32.const -0)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (set_local $2
+ (get_local $2)
+ )
+ )
+ (br_if $label$20
+ (loop $label$22 (result i32)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (f32.const -nan:0x7fffcf)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (get_local $2)
+ )
+ )
+ (if (result i64)
+ (get_local $2)
+ (i64.const 70368744177664)
+ (get_local $1)
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ (br_if $label$17
+ (i32.eqz
+ (get_local $2)
+ )
+ )
+ (get_local $2)
+ )
+ )
+ )
+ )
+ (nop)
+ )
+ (block $label$24
+ (br_if $label$2
+ (i32.eqz
+ (loop $label$25 (result i32)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (f32.const -2147483648)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block $label$26 (result i32)
+ (br_if $label$25
+ (i32.const 65449)
+ )
+ (i32.const 336860180)
+ )
+ )
+ )
+ )
+ (nop)
+ )
+ )
+ )
+ )
+ (br_if $label$2
+ (i32.eqz
+ (tee_local $2
+ (loop $label$27 (result i32)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (f32.const 70368744177664)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block $label$28 (result i32)
+ (tee_local $2
+ (tee_local $2
+ (i32.const 90)
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ (loop $label$29
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (f32.const -nan:0x7fffb7)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block $label$30
+ (set_local $2
+ (get_local $2)
+ )
+ (set_local $2
+ (tee_local $2
+ (tee_local $2
+ (if (result i32)
+ (tee_local $2
+ (tee_local $2
+ (tee_local $2
+ (tee_local $2
+ (loop $label$31 (result i32)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (f32.const 4294967296)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block (result i32)
+ (set_local $0
+ (get_local $0)
+ )
+ (br_if $label$31
+ (i32.eqz
+ (i32.const -44)
+ )
+ )
+ (call $func_1)
+ )
+ )
+ )
+ )
+ )
+ )
+ (if (result i32)
+ (i32.eqz
+ (get_local $2)
+ )
+ (block $label$32 (result i32)
+ (tee_local $2
+ (tee_local $2
+ (i32.const 30)
+ )
+ )
+ )
+ (if (result i32)
+ (block $label$33 (result i32)
+ (nop)
+ (br_if $label$33
+ (i32.const 2147483647)
+ (i32.eqz
+ (i32.const 128)
+ )
+ )
+ )
+ (block $label$34 (result i32)
+ (br_if $label$1
+ (i32.eqz
+ (get_local $2)
+ )
+ )
+ (br_if $label$34
+ (get_local $2)
+ (get_local $2)
+ )
+ )
+ (block $label$35 (result i32)
+ (if
+ (i32.eqz
+ (get_local $2)
+ )
+ (set_local $2
+ (get_local $2)
+ )
+ (br_if $label$1
+ (i32.eqz
+ (i32.const -1)
+ )
+ )
+ )
+ (br $label$3)
+ )
+ )
+ )
+ (block $label$36 (result i32)
+ (nop)
+ (i32.const -10)
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ (nop)
+ )
+ )
(return
- (get_local $2)
+ (f32.const 69)
)
)
)
- (func $func_6 (; 6 ;) (type $FUNCSIG$f) (result f32)
- (local $0 f64)
- (local $1 f64)
+ (func $func_6 (; 6 ;) (type $FUNCSIG$jddi) (param $0 f64) (param $1 f64) (param $2 i32) (result i64)
+ (local $3 f64)
(block
(if
(i32.eqz
(get_global $hangLimit)
)
(return
- (f32.const 8589934592)
+ (i64.const 3336938918731730224)
)
)
(set_global $hangLimit
@@ -230,43 +894,77 @@
)
)
)
- (call $deNan32
- (f32.neg
- (loop $label$0 (result f32)
- (block
- (if
- (i32.eqz
- (get_global $hangLimit)
- )
- (return
- (f32.const 3402823466385288598117041e14)
- )
- )
- (set_global $hangLimit
- (i32.sub
- (get_global $hangLimit)
- (i32.const 1)
+ (block $label$0 (result i64)
+ (nop)
+ (set_local $2
+ (get_local $2)
+ )
+ (if (result i64)
+ (block $label$25 (result i32)
+ (set_global $global$2
+ (get_local $1)
+ )
+ (tee_local $2
+ (tee_local $2
+ (tee_local $2
+ (tee_local $2
+ (tee_local $2
+ (get_local $2)
+ )
+ )
)
)
)
- (f32.const 1)
+ )
+ (block $label$26 (result i64)
+ (nop)
+ (return
+ (i64.const -524288)
+ )
+ )
+ (block $label$27 (result i64)
+ (nop)
+ (return
+ (i64.const -32767)
+ )
)
)
)
)
(func $func_6_invoker (; 7 ;) (type $FUNCSIG$v)
(drop
- (call $func_6)
+ (call $func_6
+ (f64.const 16384)
+ (f64.const -nan:0xfffffffffffc8)
+ (i32.const 32768)
+ )
+ )
+ )
+ (func $func_8 (; 8 ;) (param $0 i32)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return)
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
)
+ (nop)
)
- (func $func_8 (; 8 ;) (type $FUNCSIG$jif) (param $0 i32) (param $1 f32) (result i64)
+ (func $func_9 (; 9 ;) (param $0 i32) (result i64)
(block
(if
(i32.eqz
(get_global $hangLimit)
)
(return
- (i64.const 255)
+ (i64.const 1)
)
)
(set_global $hangLimit
@@ -276,40 +974,420 @@
)
)
)
- (block $label$0 (result i64)
- (nop)
- (select
- (i64.const -87)
- (block $label$1 (result i64)
- (f32.store offset=2 align=2
- (i32.and
- (i32.const -1073741824)
- (i32.const 15)
+ (loop $label$0 (result i64)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (i64.const -2147483647)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block (result i64)
+ (block $label$1
+ (block $label$2
+ (loop $label$3
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (i64.const 8589934592)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block $label$4
+ (br_if $label$3
+ (i32.eqz
+ (get_local $0)
+ )
+ )
+ (if
+ (i32.eqz
+ (loop $label$5 (result i32)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (i64.const -88)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block (result i32)
+ (block $label$6
+ (nop)
+ (set_local $0
+ (block $label$7 (result i32)
+ (nop)
+ (tee_local $0
+ (br_if $label$7
+ (tee_local $0
+ (i32.atomic.load offset=22
+ (i32.and
+ (tee_local $0
+ (get_local $0)
+ )
+ (i32.const 15)
+ )
+ )
+ )
+ (loop $label$8 (result i32)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (i64.const -65536)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (tee_local $0
+ (tee_local $0
+ (i32.const 2147483647)
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ (br_if $label$5
+ (i32.eqz
+ (i32.const 65443)
+ )
+ )
+ (i32.const -131072)
+ )
+ )
+ )
+ (block $label$9
+ (nop)
+ (nop)
+ )
+ (if
+ (i32.eqz
+ (loop $label$10 (result i32)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (i64.const -4)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block $label$11 (result i32)
+ (nop)
+ (br $label$3)
+ )
+ )
+ )
+ (block $label$12
+ (nop)
+ (nop)
+ )
+ (set_local $0
+ (tee_local $0
+ (tee_local $0
+ (i32.const -2147483648)
+ )
+ )
+ )
+ )
+ )
+ )
)
- (block $label$3 (result f32)
- (get_local $1)
+ (nop)
+ )
+ (br_if $label$0
+ (i32.eqz
+ (get_local $0)
)
)
- (i64.const 106)
)
- (call $func_3)
+ (br_if $label$0
+ (i32.eqz
+ (loop $label$13 (result i32)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (i64.const 362837742134036742)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (get_local $0)
+ )
+ )
+ )
+ (if (result i64)
+ (block $label$14 (result i32)
+ (loop $label$15
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (i64.const 4294967295)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block
+ (block $label$16
+ (call $func_2)
+ (set_local $0
+ (tee_local $0
+ (i32.const 16384)
+ )
+ )
+ )
+ (br_if $label$15
+ (i32.const 354293528)
+ )
+ (if
+ (i32.eqz
+ (br_if $label$14
+ (i32.const -1024)
+ (br_if $label$14
+ (get_local $0)
+ (if (result i32)
+ (i32.eqz
+ (br_if $label$14
+ (br_if $label$14
+ (br_if $label$14
+ (loop $label$17 (result i32)
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (i64.const 512)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (i32.const -128)
+ )
+ (i32.eqz
+ (get_local $0)
+ )
+ )
+ (br_if $label$14
+ (get_local $0)
+ (i32.const -32768)
+ )
+ )
+ (i32.eqz
+ (i32.const 1380139090)
+ )
+ )
+ )
+ (block $label$18 (result i32)
+ (loop $label$19
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (i64.const -137438953472)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block $label$20
+ (set_local $0
+ (i32.const 512)
+ )
+ (loop $label$21
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (i64.const 2199023255552)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (br_if $label$15
+ (tee_local $0
+ (f32.le
+ (f32.const 3.989732680786937e-31)
+ (f32.const 8192)
+ )
+ )
+ )
+ )
+ )
+ )
+ (block $label$22 (result i32)
+ (loop $label$23
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (i64.const 1145899072)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block $label$24
+ (set_local $0
+ (i32.const 0)
+ )
+ (loop $label$25
+ (block
+ (if
+ (i32.eqz
+ (get_global $hangLimit)
+ )
+ (return
+ (i64.const 134217728)
+ )
+ )
+ (set_global $hangLimit
+ (i32.sub
+ (get_global $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block
+ (nop)
+ (br_if $label$25
+ (i32.const 84691274)
+ )
+ (nop)
+ )
+ )
+ )
+ )
+ (get_local $0)
+ )
+ )
+ (block $label$26 (result i32)
+ (i32.const 1459819012)
+ )
+ )
+ )
+ )
+ )
+ (nop)
+ (block $label$27
+ (nop)
+ )
+ )
+ )
+ )
+ (get_local $0)
+ )
+ (i64.const -29)
+ (i64.const 7425313271795557411)
+ )
)
)
)
- (func $func_8_invoker (; 9 ;) (type $FUNCSIG$v)
+ (func $func_9_invoker (; 10 ;) (type $FUNCSIG$v)
+ (drop
+ (call $func_9
+ (i32.const -120)
+ )
+ )
+ (drop
+ (call $func_9
+ (i32.const 11571)
+ )
+ )
(drop
- (call $func_8
- (i32.const -123)
- (f32.const 48)
+ (call $func_9
+ (i32.const -1)
)
)
)
- (func $hangLimitInitializer (; 10 ;)
+ (func $hangLimitInitializer (; 11 ;)
(set_global $hangLimit
(i32.const 10)
)
)
- (func $deNan32 (; 11 ;) (param $0 f32) (result f32)
+ (func $deNan32 (; 12 ;) (param $0 f32) (result f32)
(if (result f32)
(f32.eq
(get_local $0)
@@ -319,7 +1397,7 @@
(f32.const 0)
)
)
- (func $deNan64 (; 12 ;) (param $0 f64) (result f64)
+ (func $deNan64 (; 13 ;) (param $0 f64) (result f64)
(if (result f64)
(f64.eq
(get_local $0)