diff options
-rw-r--r-- | src/ir/LocalGraph.cpp | 7 | ||||
-rw-r--r-- | src/ir/local-graph.h | 3 | ||||
-rw-r--r-- | src/passes/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/passes/MergeLocals.cpp | 220 | ||||
-rw-r--r-- | src/passes/pass.cpp | 5 | ||||
-rw-r--r-- | src/passes/passes.h | 1 | ||||
-rw-r--r-- | src/tools/fuzzing.h | 14 | ||||
-rw-r--r-- | src/tools/wasm-opt.cpp | 48 | ||||
-rw-r--r-- | test/passes/merge-locals.txt | 460 | ||||
-rw-r--r-- | test/passes/merge-locals.wast | 379 | ||||
-rw-r--r-- | test/passes/translate-to-fuzz.txt | 1362 |
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) |