summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcheck.py37
-rw-r--r--src/asm2wasm.h16
-rw-r--r--src/ast/ExpressionManipulator.cpp1
-rw-r--r--src/ast/block-utils.h66
-rw-r--r--src/ast/manipulation.h69
-rw-r--r--src/ast/type-updating.h282
-rw-r--r--src/ast_utils.h168
-rw-r--r--src/cfg/Relooper.cpp22
-rw-r--r--src/pass.h10
-rw-r--r--src/passes/CoalesceLocals.cpp11
-rw-r--r--src/passes/DeadCodeElimination.cpp42
-rw-r--r--src/passes/MergeBlocks.cpp21
-rw-r--r--src/passes/OptimizeInstructions.cpp1
-rw-r--r--src/passes/Precompute.cpp6
-rw-r--r--src/passes/Print.cpp13
-rw-r--r--src/passes/RelooperJumpThreading.cpp7
-rw-r--r--src/passes/RemoveUnusedBrs.cpp23
-rw-r--r--src/passes/RemoveUnusedNames.cpp1
-rw-r--r--src/passes/SimplifyLocals.cpp1
-rw-r--r--src/passes/Vacuum.cpp53
-rw-r--r--src/passes/pass.cpp13
-rw-r--r--src/s2wasm.h5
-rw-r--r--src/tools/asm2wasm.cpp7
-rw-r--r--src/wasm-builder.h22
-rw-r--r--src/wasm-module-building.h3
-rw-r--r--src/wasm-traversal.h29
-rw-r--r--src/wasm-validator.h57
-rw-r--r--src/wasm/wasm-s-parser.cpp2
-rw-r--r--test/dot_s/unreachable_blocks.wast8
-rw-r--r--test/passes/O.txt5
-rw-r--r--test/passes/O.wast11
-rw-r--r--test/passes/coalesce-locals-learning.txt16
-rw-r--r--test/passes/coalesce-locals.txt30
-rw-r--r--test/passes/dce.txt155
-rw-r--r--test/passes/dce.wast113
-rw-r--r--test/passes/dce_vacuum.bin.txt26
-rw-r--r--test/passes/dce_vacuum.txt4
-rw-r--r--test/passes/precompute.txt24
-rw-r--r--test/passes/precompute.wast24
-rw-r--r--test/passes/precompute_coalesce-locals_vacuum.txt5
-rw-r--r--test/passes/remove-unused-names_merge-blocks.txt49
-rw-r--r--test/passes/remove-unused-names_merge-blocks.wast12
-rw-r--r--test/passes/remove-unused-names_precompute.txt25
-rw-r--r--test/passes/remove-unused-names_precompute.wast28
-rw-r--r--test/passes/remove-unused-names_vacuum.txt8
-rw-r--r--test/passes/remove-unused-names_vacuum.wast7
-rw-r--r--test/passes/rereloop_dce_remove-unused-brs_remove-unused-names_simplify-locals-nostructure_vacuum_reorder-locals_coalesce-locals_simplify-locals_reorder-locals_merge-blocks_remove-unused-brs_merge-blocks_vacuum.txt12
-rw-r--r--test/passes/vacuum.txt97
-rw-r--r--test/passes/vacuum.wast46
-rw-r--r--test/unreachable-import_wasm-only.fromasm14
-rw-r--r--test/unreachable-import_wasm-only.fromasm.clamp14
-rw-r--r--test/unreachable-import_wasm-only.fromasm.imprecise14
-rw-r--r--test/wasm-only.asm.js134
-rw-r--r--test/wasm-only.fromasm331
-rw-r--r--test/wasm-only.fromasm.clamp331
-rw-r--r--test/wasm-only.fromasm.clamp.no-opts592
-rw-r--r--test/wasm-only.fromasm.imprecise331
-rw-r--r--test/wasm-only.fromasm.imprecise.no-opts592
-rw-r--r--test/wasm-only.fromasm.no-opts592
59 files changed, 4275 insertions, 363 deletions
diff --git a/check.py b/check.py
index ca5d74efd..4c84a004d 100755
--- a/check.py
+++ b/check.py
@@ -125,16 +125,29 @@ for asm in tests:
cmd += ['--wasm-only']
wasm = os.path.join(options.binaryen_test, wasm)
print '..', asm, wasm
- actual = run_command(cmd)
- # verify output
- if not os.path.exists(wasm):
- fail_with_error('output .wast file %s does not exist' % wasm)
- expected = open(wasm, 'rb').read()
- if actual != expected:
- fail(actual, expected)
-
- binary_format_check(wasm, verify_final_result=False)
+ def do_asm2wasm_test():
+ actual = run_command(cmd)
+
+ # verify output
+ if not os.path.exists(wasm):
+ fail_with_error('output .wast file %s does not exist' % wasm)
+ expected = open(wasm, 'rb').read()
+ if actual != expected:
+ fail(actual, expected)
+
+ binary_format_check(wasm, verify_final_result=False)
+
+ # test both normally and with pass debug (so each inter-pass state is validated)
+ old_pass_debug = os.environ.get('BINARYEN_PASS_DEBUG')
+ try:
+ os.environ['BINARYEN_PASS_DEBUG'] = '1'
+ do_asm2wasm_test()
+ del os.environ['BINARYEN_PASS_DEBUG']
+ do_asm2wasm_test()
+ finally:
+ if old_pass_debug is not None:
+ os.environ['BINARYEN_PASS_DEBUG'] = old_pass_debug
# verify in wasm
if options.interpreter:
@@ -280,6 +293,11 @@ for t in spec_tests:
cmd = cmd + (extra.get(os.path.basename(wast)) or [])
return run_command(cmd, stderr=subprocess.PIPE)
+ def run_opt_test(wast):
+ # check optimization validation
+ cmd = WASM_OPT + [wast, '-O']
+ run_command(cmd)
+
def check_expected(actual, expected):
if expected and os.path.exists(expected):
expected = open(expected).read()
@@ -335,6 +353,7 @@ for t in spec_tests:
split_num += 1
with open('split.wast', 'w') as o: o.write(module + '\n' + '\n'.join(asserts))
run_spec_test('split.wast') # before binary stuff - just check it's still ok split out
+ run_opt_test('split.wast') # also that our optimizer doesn't break on it
result_wast = binary_format_check('split.wast', verify_final_result=False)
# add the asserts, and verify that the test still passes
open(result_wast, 'a').write('\n' + '\n'.join(asserts))
diff --git a/src/asm2wasm.h b/src/asm2wasm.h
index 6327920fe..16e7c95e1 100644
--- a/src/asm2wasm.h
+++ b/src/asm2wasm.h
@@ -35,7 +35,6 @@
#include "wasm-builder.h"
#include "wasm-emscripten.h"
#include "wasm-printing.h"
-#include "wasm-validator.h"
#include "wasm-module-building.h"
namespace wasm {
@@ -1339,6 +1338,12 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
add->left = parent->builder.makeConst(Literal((int32_t)parent->functionTableStarts[tableName]));
}
}
+
+ void visitFunction(Function* curr) {
+ // changing call types requires we percolate types, and drop stuff.
+ // we do this in this pass so that we don't look broken between passes
+ AutoDrop().walkFunctionInModule(curr, getModule());
+ }
};
// apply debug info, reducing intrinsic calls into annotations on the ast nodes
@@ -1400,9 +1405,9 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
passRunner.setDebug(true);
passRunner.setValidateGlobally(false);
}
+ // finalizeCalls also does autoDrop, which is crucial for the non-optimizing case,
+ // so that the output of the first pass is valid
passRunner.add<FinalizeCalls>(this);
- passRunner.add<ReFinalize>(); // FinalizeCalls changes call types, need to percolate
- passRunner.add<AutoDrop>(); // FinalizeCalls may cause us to require additional drops
if (legalizeJavaScriptFFI) {
passRunner.add("legalize-js-interface");
}
@@ -1551,8 +1556,6 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
body->finalize();
func->body = body;
}
-
- assert(WasmValidator().validate(wasm));
}
Function* Asm2WasmBuilder::processFunction(Ref ast) {
@@ -2489,7 +2492,6 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
}
top->list.push_back(br);
- top->finalize();
for (unsigned i = 0; i < cases->size(); i++) {
Ref curr = cases[i];
@@ -2564,7 +2566,6 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
top->name = name;
next->list.push_back(top);
next->list.push_back(case_);
- next->finalize();
top = next;
nameMapper.popLabelName(name);
}
@@ -2580,7 +2581,6 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
first->ifFalse = builder.makeBreak(br->default_);
brHolder->list.push_back(chain);
- brHolder->finalize();
}
breakStack.pop_back();
diff --git a/src/ast/ExpressionManipulator.cpp b/src/ast/ExpressionManipulator.cpp
index fb861f0d8..160ff55e1 100644
--- a/src/ast/ExpressionManipulator.cpp
+++ b/src/ast/ExpressionManipulator.cpp
@@ -147,6 +147,7 @@ void ExpressionManipulator::spliceIntoBlock(Block* block, Index index, Expressio
}
list[index] = add;
}
+ block->finalize(block->type);
}
} // namespace wasm
diff --git a/src/ast/block-utils.h b/src/ast/block-utils.h
new file mode 100644
index 000000000..7b1b9dcff
--- /dev/null
+++ b/src/ast/block-utils.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2017 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.
+ */
+
+#ifndef wasm_ast_block_h
+#define wasm_ast_block_h
+
+#include "literal.h"
+#include "wasm.h"
+#include "ast_utils.h"
+
+namespace wasm {
+
+namespace BlockUtils {
+ // if a block has just one element, it can often be replaced
+ // with that content
+ template<typename T>
+ inline Expression* simplifyToContents(Block* block, T* parent, bool allowTypeChange = false) {
+ auto& list = block->list;
+ if (list.size() == 1 && !BreakSeeker::has(list[0], block->name)) {
+ // just one element. try to replace the block
+ auto* singleton = list[0];
+ auto sideEffects = EffectAnalyzer(parent->getPassOptions(), singleton).hasSideEffects();
+ if (!sideEffects && !isConcreteWasmType(singleton->type)) {
+ // no side effects, and singleton is not returning a value, so we can throw away
+ // the block and its contents, basically
+ return Builder(*parent->getModule()).replaceWithIdenticalType(block);
+ } else if (block->type == singleton->type || allowTypeChange) {
+ return singleton;
+ } else {
+ // (side effects +) type change, must be block with declared value but inside is unreachable
+ // (if both concrete, must match, and since no name on block, we can't be
+ // branched to, so if singleton is unreachable, so is the block)
+ assert(isConcreteWasmType(block->type) && singleton->type == unreachable);
+ // we could replace with unreachable, but would need to update all
+ // the parent's types
+ }
+ } else if (list.size() == 0) {
+ ExpressionManipulator::nop(block);
+ }
+ return block;
+ }
+
+ // similar, but when we allow the type to change while doing so
+ template<typename T>
+ inline Expression* simplifyToContentsWithPossibleTypeChange(Block* block, T* parent) {
+ return simplifyToContents(block, parent, true);
+ }
+};
+
+} // namespace wasm
+
+#endif // wasm_ast_block_h
+
diff --git a/src/ast/manipulation.h b/src/ast/manipulation.h
new file mode 100644
index 000000000..9e01e4c74
--- /dev/null
+++ b/src/ast/manipulation.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017 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.
+ */
+
+#ifndef wasm_ast_manipulation_h
+#define wasm_ast_manipulation_h
+
+#include "wasm.h"
+
+namespace wasm {
+
+struct ExpressionManipulator {
+ // Re-use a node's memory. This helps avoid allocation when optimizing.
+ template<typename InputType, typename OutputType>
+ static OutputType* convert(InputType *input) {
+ static_assert(sizeof(OutputType) <= sizeof(InputType),
+ "Can only convert to a smaller size Expression node");
+ input->~InputType(); // arena-allocaed, so no destructor, but avoid UB.
+ OutputType* output = (OutputType*)(input);
+ new (output) OutputType;
+ return output;
+ }
+
+ // Convenience method for nop, which is a common conversion
+ template<typename InputType>
+ static Nop* nop(InputType* target) {
+ return convert<InputType, Nop>(target);
+ }
+
+ // Convert a node that allocates
+ template<typename InputType, typename OutputType>
+ static OutputType* convert(InputType *input, MixedArena& allocator) {
+ assert(sizeof(OutputType) <= sizeof(InputType));
+ input->~InputType(); // arena-allocaed, so no destructor, but avoid UB.
+ OutputType* output = (OutputType*)(input);
+ new (output) OutputType(allocator);
+ return output;
+ }
+
+ using CustomCopier = std::function<Expression*(Expression*)>;
+ static Expression* flexibleCopy(Expression* original, Module& wasm, CustomCopier custom);
+
+ static Expression* copy(Expression* original, Module& wasm) {
+ auto copy = [](Expression* curr) {
+ return nullptr;
+ };
+ return flexibleCopy(original, wasm, copy);
+ }
+
+ // Splice an item into the middle of a block's list
+ static void spliceIntoBlock(Block* block, Index index, Expression* add);
+};
+
+} // wasm
+
+#endif // wams_ast_manipulation_h
+
diff --git a/src/ast/type-updating.h b/src/ast/type-updating.h
new file mode 100644
index 000000000..b8988761a
--- /dev/null
+++ b/src/ast/type-updating.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2017 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.
+ */
+
+#ifndef wasm_ast_type_updating_h
+#define wasm_ast_type_updating_h
+
+#include "wasm-traversal.h"
+
+namespace wasm {
+
+// a class that tracks type dependencies between nodes, letting you
+// update types efficiently when removing and altering code.
+// altering code can alter types in the following way:
+// * removing a break can make a block unreachable, if nothing else
+// reaches it
+// * altering the type of a child to unreachable can make the parent
+// unreachable
+struct TypeUpdater : public ExpressionStackWalker<TypeUpdater, UnifiedExpressionVisitor<TypeUpdater>> {
+ // Part 1: Scanning
+
+ // track names to their blocks, so that when we remove a break to
+ // a block, we know how to find it if we need to update it
+ struct BlockInfo {
+ Block* block = nullptr;
+ int numBreaks = 0;
+ };
+ std::map<Name, BlockInfo> blockInfos;
+
+ // track the parent of each node, as child type changes may lead to
+ // unreachability
+ std::map<Expression*, Expression*> parents;
+
+ void visitExpression(Expression* curr) {
+ if (expressionStack.size() > 1) {
+ parents[curr] = expressionStack[expressionStack.size() - 2];
+ } else {
+ parents[curr] = nullptr; // this is the top level
+ }
+ // discover block/break relationships
+ if (auto* block = curr->dynCast<Block>()) {
+ if (block->name.is()) {
+ blockInfos[block->name].block = block;
+ }
+ } else if (auto* br = curr->dynCast<Break>()) {
+ // ensure info exists, discoverBreaks can then fill it
+ blockInfos[br->name];
+ } else if (auto* sw = curr->dynCast<Switch>()) {
+ // ensure info exists, discoverBreaks can then fill it
+ for (auto target : sw->targets) {
+ blockInfos[target];
+ }
+ blockInfos[sw->default_];
+ }
+ // add a break to the info, for break and switch
+ discoverBreaks(curr, +1);
+ }
+
+ // Part 2: Updating
+
+ // Node replacements, additions, removals and type changes should be noted. An
+ // exception is nodes you know will never be looked at again.
+
+ // note the replacement of one node with another. this should be called
+ // after performing the replacement.
+ // this does *not* look into the node. you should do so yourself if necessary
+ void noteReplacement(Expression* from, Expression* to) {
+ auto parent = parents[from];
+ noteRemoval(from);
+ // if we are replacing with a child, i.e. a node that was already present
+ // in the ast, then we just have a type and parent to update
+ if (parents.find(to) != parents.end()) {
+ parents[to] = parent;
+ if (from->type != to->type) {
+ propagateTypesUp(to);
+ }
+ } else {
+ noteAddition(to, parent, from);
+ }
+ }
+
+ // note the removal of a node
+ void noteRemoval(Expression* curr) {
+ noteRemovalOrAddition(curr, nullptr);
+ parents.erase(curr);
+ }
+
+ // note the removal of a node and all its children
+ void noteRecursiveRemoval(Expression* curr) {
+ struct Recurser : public PostWalker<Recurser, UnifiedExpressionVisitor<Recurser>> {
+ TypeUpdater& parent;
+
+ Recurser(TypeUpdater& parent, Expression* root) : parent(parent) {
+ walk(root);
+ }
+
+ void visitExpression(Expression* curr) {
+ parent.noteRemoval(curr);
+ }
+ };
+
+ Recurser(*this, curr);
+ }
+
+ void noteAddition(Expression* curr, Expression* parent, Expression* previous = nullptr) {
+ assert(parents.find(curr) == parents.end()); // must not already exist
+ noteRemovalOrAddition(curr, parent);
+ // if we didn't replace with the exact same type, propagate types up
+ if (!(previous && previous->type == curr->type)) {
+ propagateTypesUp(curr);
+ }
+ }
+
+ // if parent is nullptr, this is a removal
+ void noteRemovalOrAddition(Expression* curr, Expression* parent) {
+ parents[curr] = parent;
+ discoverBreaks(curr, parent ? +1 : -1);
+ }
+
+ // adds (or removes) breaks depending on break/switch contents
+ void discoverBreaks(Expression* curr, int change) {
+ if (auto* br = curr->dynCast<Break>()) {
+ if (!(br->value && br->value->type == unreachable) &&
+ !(br->condition && br->condition->type == unreachable)) {
+ noteBreakChange(br->name, change, br->value);
+ }
+ } else if (auto* sw = curr->dynCast<Switch>()) {
+ if (!(sw->value && sw->value->type == unreachable) &&
+ sw->condition->type != unreachable) {
+ applySwitchChanges(sw, change);
+ }
+ }
+ }
+
+ void applySwitchChanges(Switch* sw, int change) {
+ std::set<Name> seen;
+ for (auto target : sw->targets) {
+ if (seen.insert(target).second) {
+ noteBreakChange(target, change, sw->value);
+ }
+ }
+ if (seen.insert(sw->default_).second) {
+ noteBreakChange(sw->default_, change, sw->value);
+ }
+ }
+
+ // note the addition of a node
+ void noteBreakChange(Name name, int change, Expression* value) {
+ auto iter = blockInfos.find(name);
+ if (iter == blockInfos.end()) {
+ return; // we can ignore breaks to loops
+ }
+ auto& info = iter->second;
+ info.numBreaks += change;
+ assert(info.numBreaks >= 0);
+ auto* block = info.block;
+ if (block) { // if to a loop, can ignore
+ if (info.numBreaks == 0) {
+ // dropped to 0! the block may now be unreachable. that
+ // requires that it doesn't have a fallthrough
+ makeBlockUnreachableIfNoFallThrough(block);
+ } else if (change == 1 && info.numBreaks == 1) {
+ // bumped to 1! the block may now be reachable
+ if (block->type != unreachable) {
+ return; // was already reachable, had a fallthrough
+ }
+ changeTypeTo(block, value ? value->type : none);
+ }
+ }
+ }
+
+ // alters the type of a node to a new type.
+ // this propagates the type change through all the parents.
+ void changeTypeTo(Expression* curr, WasmType newType) {
+ if (curr->type == newType) return; // nothing to do
+ curr->type = newType;
+ propagateTypesUp(curr);
+ }
+
+ // given a node that has a new type, or is a new node, update
+ // all the parents accordingly. the existence of the node and
+ // any changes to it already occurred, this just updates the
+ // parents following that. i.e., nothing is done to the
+ // node we start on, it's done.
+ // the one thing we need to do here is propagate unreachability,
+ // no other change is possible
+ void propagateTypesUp(Expression* curr) {
+ if (curr->type != unreachable) return;
+ while (1) {
+ auto* child = curr;
+ curr = parents[child];
+ if (!curr) return;
+ // if a child of a break/switch is now unreachable, the
+ // break may no longer be taken. note that if we get here,
+ // this is an actually new unreachable child of the
+ // node, so if there is just 1 such child, it is us, and
+ // we are newly unreachable
+ if (auto* br = curr->dynCast<Break>()) {
+ int unreachableChildren = 0;
+ if (br->value && br->value->type == unreachable) unreachableChildren++;
+ if (br->condition && br->condition->type == unreachable) unreachableChildren++;
+ if (unreachableChildren == 1) {
+ // the break is no longer taken
+ noteBreakChange(br->name, -1, br->value);
+ }
+ } else if (auto* sw = curr->dynCast<Switch>()) {
+ int unreachableChildren = 0;
+ if (sw->value && sw->value->type == unreachable) unreachableChildren++;
+ if (sw->condition->type == unreachable) unreachableChildren++;
+ if (unreachableChildren == 1) {
+ applySwitchChanges(sw, -1);
+ }
+ }
+ // get ready to apply unreachability to this node
+ if (curr->type == unreachable) {
+ return; // already unreachable, stop here
+ }
+ // most nodes become unreachable if a child is unreachable,
+ // but exceptions exists
+ if (auto* block = curr->dynCast<Block>()) {
+ // if the block has breaks, it can keep its type
+ if (!block->name.is() || blockInfos[block->name].numBreaks == 0) {
+ curr->type = unreachable;
+ } else {
+ return; // did not turn
+ }
+ } else if (auto* iff = curr->dynCast<If>()) {
+ // may not be unreachable if just one side is
+ iff->finalize();
+ if (curr->type != unreachable) {
+ return; // did not turn
+ }
+ } else {
+ curr->type = unreachable;
+ }
+ }
+ }
+
+ // efficiently update the type of a block, given the data we know. this
+ // can remove a concrete type and turn the block unreachable when it is
+ // unreachable, and it does this efficiently, without scanning the full
+ // contents
+ void maybeUpdateTypeToUnreachable(Block* curr) {
+ if (!isConcreteWasmType(curr->type)) {
+ return; // nothing concrete to change to unreachable
+ }
+ if (curr->name.is() && blockInfos[curr->name].numBreaks > 0) {
+ return;// has a break, not unreachable
+ }
+ // look for a fallthrough
+ makeBlockUnreachableIfNoFallThrough(curr);
+ }
+
+ void makeBlockUnreachableIfNoFallThrough(Block* curr) {
+ if (curr->type == unreachable) {
+ return; // no change possible
+ }
+ for (auto* child : curr->list) {
+ if (child->type == unreachable) {
+ // no fallthrough, this block is now unreachable
+ changeTypeTo(curr, unreachable);
+ return;
+ }
+ }
+ }
+};
+
+} // namespace wasm
+
+#endif // wasm_ast_type_updating_h
diff --git a/src/ast_utils.h b/src/ast_utils.h
index 5a7c40630..aa4120569 100644
--- a/src/ast_utils.h
+++ b/src/ast_utils.h
@@ -42,10 +42,18 @@ struct BreakSeeker : public PostWalker<BreakSeeker> {
}
void visitBreak(Break *curr) {
+ // ignore an unreachable break
+ if (curr->condition && curr->condition->type == unreachable) return;
+ if (curr->value && curr->value->type == unreachable) return;
+ // check the break
if (curr->name == target) noteFound(curr->value);
}
void visitSwitch(Switch *curr) {
+ // ignore an unreachable switch
+ if (curr->condition->type == unreachable) return;
+ if (curr->value && curr->value->type == unreachable) return;
+ // check the switch
for (auto name : curr->targets) {
if (name == target) noteFound(curr->value);
}
@@ -273,50 +281,6 @@ struct Measurer : public PostWalker<Measurer, UnifiedExpressionVisitor<Measurer>
}
};
-// Manipulate expressions
-
-struct ExpressionManipulator {
- // Re-use a node's memory. This helps avoid allocation when optimizing.
- template<typename InputType, typename OutputType>
- static OutputType* convert(InputType *input) {
- static_assert(sizeof(OutputType) <= sizeof(InputType),
- "Can only convert to a smaller size Expression node");
- input->~InputType(); // arena-allocaed, so no destructor, but avoid UB.
- OutputType* output = (OutputType*)(input);
- new (output) OutputType;
- return output;
- }
-
- // Convenience method for nop, which is a common conversion
- template<typename InputType>
- static void nop(InputType* target) {
- convert<InputType, Nop>(target);
- }
-
- // Convert a node that allocates
- template<typename InputType, typename OutputType>
- static OutputType* convert(InputType *input, MixedArena& allocator) {
- assert(sizeof(OutputType) <= sizeof(InputType));
- input->~InputType(); // arena-allocaed, so no destructor, but avoid UB.
- OutputType* output = (OutputType*)(input);
- new (output) OutputType(allocator);
- return output;
- }
-
- using CustomCopier = std::function<Expression*(Expression*)>;
- static Expression* flexibleCopy(Expression* original, Module& wasm, CustomCopier custom);
-
- static Expression* copy(Expression* original, Module& wasm) {
- auto copy = [](Expression* curr) {
- return nullptr;
- };
- return flexibleCopy(original, wasm, copy);
- }
-
- // Splice an item into the middle of a block's list
- static void spliceIntoBlock(Block* block, Index index, Expression* add);
-};
-
struct ExpressionAnalyzer {
// Given a stack of expressions, checks if the topmost is used as a result.
// For example, if the parent is a block and the node is before the last position,
@@ -357,11 +321,102 @@ struct ExpressionAnalyzer {
static uint32_t hash(Expression* curr);
};
-// Finalizes a node
-
+// Re-Finalizes all node types
+// This removes "unnecessary' block/if/loop types, i.e., that are added
+// specifically, as in
+// (block i32 (unreachable))
+// vs
+// (block (unreachable))
+// This converts to the latter form.
struct ReFinalize : public WalkerPass<PostWalker<ReFinalize>> {
+ bool isFunctionParallel() override { return true; }
+
+ Pass* create() override { return new ReFinalize; }
+
ReFinalize() { name = "refinalize"; }
+ // block finalization is O(bad) if we do each block by itself, so do it in bulk,
+ // tracking break value types so we just do a linear pass
+
+ std::map<Name, WasmType> breakValues;
+
+ void visitBlock(Block *curr) {
+ // do this quickly, without any validation
+ if (curr->name.is()) {
+ auto iter = breakValues.find(curr->name);
+ if (iter != breakValues.end()) {
+ // there is a break to here
+ curr->type = iter->second;
+ return;
+ }
+ }
+ // nothing branches here
+ if (curr->list.size() > 0) {
+ // if we have an unreachable child, we are unreachable
+ // (we don't need to recurse into children, they can't
+ // break to us)
+ for (auto* child : curr->list) {
+ if (child->type == unreachable) {
+ curr->type = unreachable;
+ return;
+ }
+ }
+ // children are reachable, so last element determines type
+ curr->type = curr->list.back()->type;
+ } else {
+ curr->type = none;
+ }
+ }
+ void visitIf(If *curr) { curr->finalize(); }
+ void visitLoop(Loop *curr) { curr->finalize(); }
+ void visitBreak(Break *curr) {
+ curr->finalize();
+ if (curr->value && curr->value->type == unreachable) {
+ return; // not an actual break
+ }
+ if (curr->condition && curr->condition->type == unreachable) {
+ return; // not an actual break
+ }
+ breakValues[curr->name] = getValueType(curr->value);
+ }
+ void visitSwitch(Switch *curr) {
+ curr->finalize();
+ if (curr->condition->type == unreachable || (curr->value && curr->value->type == unreachable)) {
+ return; // not an actual break
+ }
+ auto valueType = getValueType(curr->value);
+ for (auto target : curr->targets) {
+ breakValues[target] = valueType;
+ }
+ breakValues[curr->default_] = valueType;
+ }
+ void visitCall(Call *curr) { curr->finalize(); }
+ void visitCallImport(CallImport *curr) { curr->finalize(); }
+ void visitCallIndirect(CallIndirect *curr) { curr->finalize(); }
+ void visitGetLocal(GetLocal *curr) { curr->finalize(); }
+ void visitSetLocal(SetLocal *curr) { curr->finalize(); }
+ void visitGetGlobal(GetGlobal *curr) { curr->finalize(); }
+ void visitSetGlobal(SetGlobal *curr) { curr->finalize(); }
+ void visitLoad(Load *curr) { curr->finalize(); }
+ void visitStore(Store *curr) { curr->finalize(); }
+ void visitConst(Const *curr) { curr->finalize(); }
+ void visitUnary(Unary *curr) { curr->finalize(); }
+ void visitBinary(Binary *curr) { curr->finalize(); }
+ void visitSelect(Select *curr) { curr->finalize(); }
+ void visitDrop(Drop *curr) { curr->finalize(); }
+ void visitReturn(Return *curr) { curr->finalize(); }
+ void visitHost(Host *curr) { curr->finalize(); }
+ void visitNop(Nop *curr) { curr->finalize(); }
+ void visitUnreachable(Unreachable *curr) { curr->finalize(); }
+
+ WasmType getValueType(Expression* value) {
+ return value && value->type != unreachable ? value->type : none;
+ }
+};
+
+// Re-finalize a single node. This is slow, if you want to refinalize
+// an entire ast, use ReFinalize
+struct ReFinalizeNode : public Visitor<ReFinalizeNode> {
void visitBlock(Block *curr) { curr->finalize(); }
void visitIf(If *curr) { curr->finalize(); }
void visitLoop(Loop *curr) { curr->finalize(); }
@@ -385,10 +440,21 @@ struct ReFinalize : public WalkerPass<PostWalker<ReFinalize>> {
void visitHost(Host *curr) { curr->finalize(); }
void visitNop(Nop *curr) { curr->finalize(); }
void visitUnreachable(Unreachable *curr) { curr->finalize(); }
+
+ // given a stack of nested expressions, update them all from child to parent
+ static void updateStack(std::vector<Expression*>& expressionStack) {
+ for (int i = int(expressionStack.size()) - 1; i >= 0; i--) {
+ auto* curr = expressionStack[i];
+ ReFinalizeNode().visit(curr);
+ }
+ }
};
// Adds drop() operations where necessary. This lets you not worry about adding drop when
// generating code.
+// This also refinalizes before and after, as dropping can change types, and depends
+// on types being cleaned up - no unnecessary block/if/loop types (see refinalize)
+// TODO: optimize that, interleave them
struct AutoDrop : public WalkerPass<ExpressionStackWalker<AutoDrop>> {
bool isFunctionParallel() override { return true; }
@@ -410,10 +476,7 @@ struct AutoDrop : public WalkerPass<ExpressionStackWalker<AutoDrop>> {
}
void reFinalize() {
- for (int i = int(expressionStack.size()) - 1; i >= 0; i--) {
- auto* curr = expressionStack[i];
- ReFinalize().visit(curr);
- }
+ ReFinalizeNode::updateStack(expressionStack);
}
void visitBlock(Block* curr) {
@@ -442,10 +505,13 @@ struct AutoDrop : public WalkerPass<ExpressionStackWalker<AutoDrop>> {
}
}
- void visitFunction(Function* curr) {
+ void doWalkFunction(Function* curr) {
+ ReFinalize().walkFunctionInModule(curr, getModule());
+ walk(curr->body);
if (curr->result == none && isConcreteWasmType(curr->body->type)) {
curr->body = Builder(*getModule()).makeDrop(curr->body);
}
+ ReFinalize().walkFunctionInModule(curr, getModule());
}
};
diff --git a/src/cfg/Relooper.cpp b/src/cfg/Relooper.cpp
index a900c6608..9fcc9c8be 100644
--- a/src/cfg/Relooper.cpp
+++ b/src/cfg/Relooper.cpp
@@ -84,6 +84,7 @@ static wasm::Expression* HandleFollowupMultiples(wasm::Expression* Ret, Shape* P
} else {
for (auto* Entry : Loop->Entries) {
Curr->name = Builder.getBlockBreakName(Entry->Id);
+ Curr->finalize();
auto* Outer = Builder.makeBlock(Curr);
Outer->finalize(); // TODO: not really necessary
Curr = Outer;
@@ -210,6 +211,10 @@ wasm::Expression* Block::Render(RelooperBuilder& Builder, bool InLoop) {
// We'll emit a chain of if-elses
wasm::If* CurrIf = nullptr;
+ // we build an if, then add a child, then add a child to that, etc., so we must
+ // finalize them in reverse order
+ std::vector<wasm::If*> finalizeStack;
+
wasm::Expression* RemainingConditions = nullptr;
for (BlockBranchMap::iterator iter = ProcessedBranchesOut.begin();; iter++) {
@@ -244,6 +249,7 @@ wasm::Expression* Block::Render(RelooperBuilder& Builder, bool InLoop) {
wasm::Expression* Now;
if (RemainingConditions) {
Now = Builder.makeIf(RemainingConditions, CurrContent);
+ finalizeStack.push_back(Now->cast<wasm::If>());
} else {
Now = CurrContent;
}
@@ -256,6 +262,7 @@ wasm::Expression* Block::Render(RelooperBuilder& Builder, bool InLoop) {
}
} else {
auto* Now = Builder.makeIf(Details->Condition, CurrContent);
+ finalizeStack.push_back(Now);
if (!CurrIf) {
assert(!Root);
Root = CurrIf = Now;
@@ -275,6 +282,14 @@ wasm::Expression* Block::Render(RelooperBuilder& Builder, bool InLoop) {
}
if (IsDefault) break;
}
+
+ // finalize the if-chains
+ while (finalizeStack.size() > 0) {
+ wasm::If* curr = finalizeStack.back();
+ finalizeStack.pop_back();
+ curr->finalize();
+ }
+
} else {
// Emit a switch
auto Base = std::string("switch$") + std::to_string(Id);
@@ -365,11 +380,13 @@ wasm::Expression* MultipleShape::Render(RelooperBuilder& Builder, bool InLoop) {
// TODO: consider switch
// emit an if-else chain
wasm::If *FirstIf = nullptr, *CurrIf = nullptr;
+ std::vector<wasm::If*> finalizeStack;
for (IdShapeMap::iterator iter = InnerMap.begin(); iter != InnerMap.end(); iter++) {
auto* Now = Builder.makeIf(
Builder.makeCheckLabel(iter->first),
iter->second->Render(Builder, InLoop)
);
+ finalizeStack.push_back(Now);
if (!CurrIf) {
FirstIf = CurrIf = Now;
} else {
@@ -378,6 +395,11 @@ wasm::Expression* MultipleShape::Render(RelooperBuilder& Builder, bool InLoop) {
CurrIf = Now;
}
}
+ while (finalizeStack.size() > 0) {
+ wasm::If* curr = finalizeStack.back();
+ finalizeStack.pop_back();
+ curr->finalize();
+ }
wasm::Expression* Ret = Builder.makeBlock(FirstIf);
Ret = HandleFollowupMultiples(Ret, this, Builder, InLoop);
if (Next) {
diff --git a/src/pass.h b/src/pass.h
index 513979954..198d5dcb5 100644
--- a/src/pass.h
+++ b/src/pass.h
@@ -131,6 +131,16 @@ struct PassRunner {
isNested = nested;
}
+ // BINARYEN_PASS_DEBUG is a convenient commandline way to log out the toplevel passes, their times,
+ // and validate between each pass.
+ // (we don't recurse pass debug into sub-passes, as it doesn't help anyhow and
+ // also is bad for e.g. printing which is a pass)
+ // this method returns whether we are in passDebug mode, and which value:
+ // 1: run pass by pass, validating in between
+ // 2: also save the last pass, so it breakage happens we can print the last one
+ // 3: also dump out byn-* files for each pass
+ static int getPassDebug();
+
protected:
bool isNested = false;
diff --git a/src/passes/CoalesceLocals.cpp b/src/passes/CoalesceLocals.cpp
index ed075c57f..2828c9955 100644
--- a/src/passes/CoalesceLocals.cpp
+++ b/src/passes/CoalesceLocals.cpp
@@ -159,7 +159,7 @@ struct CoalesceLocals : public WalkerPass<CFGWalker<CoalesceLocals, Visitor<Coal
auto* curr = (*currp)->cast<GetLocal>();
// if in unreachable code, ignore
if (!self->currBasicBlock) {
- ExpressionManipulator::convert<GetLocal, Unreachable>(curr);
+ *currp = Builder(*self->getModule()).replaceWithIdenticalType(curr);
return;
}
self->currBasicBlock->contents.actions.emplace_back(Action::Get, curr->index, currp);
@@ -169,11 +169,7 @@ struct CoalesceLocals : public WalkerPass<CFGWalker<CoalesceLocals, Visitor<Coal
auto* curr = (*currp)->cast<SetLocal>();
// if in unreachable code, ignore
if (!self->currBasicBlock) {
- if (curr->isTee()) {
- ExpressionManipulator::convert<SetLocal, Unreachable>(curr);
- } else {
- ExpressionManipulator::nop(curr);
- }
+ *currp = Builder(*self->getModule()).replaceWithIdenticalType(curr);
return;
}
self->currBasicBlock->contents.actions.emplace_back(Action::Set, curr->index, currp);
@@ -625,8 +621,9 @@ static void removeIfCopy(Expression** origin, SetLocal* set, If* iff, Expression
// replace the origin with the if, and sink the set into the other non-copying arm
*origin = iff;
set->value = other;
+ set->finalize();
other = set;
- if (!set->isTee()) {
+ if (!isConcreteWasmType(set->type)) {
// we don't need the copy at all
copy = nullptr;
if (!iff->ifTrue) {
diff --git a/src/passes/DeadCodeElimination.cpp b/src/passes/DeadCodeElimination.cpp
index 0555ff1f0..42f86ba45 100644
--- a/src/passes/DeadCodeElimination.cpp
+++ b/src/passes/DeadCodeElimination.cpp
@@ -32,6 +32,8 @@
#include <pass.h>
#include <ast_utils.h>
#include <wasm-builder.h>
+#include <ast/block-utils.h>
+#include <ast/type-updating.h>
namespace wasm {
@@ -40,11 +42,23 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>>
Pass* create() override { return new DeadCodeElimination; }
+ // as we remove code, we must keep the types of other nodes valid
+ TypeUpdater typeUpdater;
+
+ Expression* replaceCurrent(Expression* expression) {
+ auto* old = getCurrent();
+ WalkerPass<PostWalker<DeadCodeElimination>>::replaceCurrent(expression);
+ // also update the type updater
+ typeUpdater.noteReplacement(old, expression);
+ return expression;
+ }
+
// whether the current code is actually reachable
bool reachable;
void doWalkFunction(Function* func) {
reachable = true;
+ typeUpdater.walk(func->body);
walk(func->body);
}
@@ -160,10 +174,8 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>>
// see https://github.com/WebAssembly/spec/issues/355
if (!(isConcreteWasmType(block->type) && block->list[i]->type == none)) {
block->list.resize(i + 1);
- // note that we do *not* finalize here. it is incorrect to re-finalize a block
- // after removing elements, as it may no longer have branches to it that would
- // determine its type, so re-finalizing would just wipe out an existing type
- // that it had.
+ // note that we still walk the children, so typeUpdater will already
+ // note they are being removed, and we don't need to do that here
}
}
}
@@ -175,8 +187,11 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>>
reachable = reachable || reachableBreaks.count(curr->name);
reachableBreaks.erase(curr->name);
}
- if (curr->list.size() == 1 && isDead(curr->list[0]) && !BreakSeeker::has(curr->list[0], curr->name)) {
- replaceCurrent(curr->list[0]);
+ if (curr->list.size() == 1 && isDead(curr->list[0])) {
+ replaceCurrent(BlockUtils::simplifyToContentsWithPossibleTypeChange(curr, this));
+ } else {
+ // the block may have had a type, but can now be unreachable, which allows more reduction outside
+ typeUpdater.maybeUpdateTypeToUnreachable(curr);
}
}
@@ -213,13 +228,18 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>>
if (isDead(curr->condition)) {
replaceCurrent(curr->condition);
}
+ // the if may have had a type, but can now be unreachable, which allows more reduction outside
+ curr->finalize();
}
static void scan(DeadCodeElimination* self, Expression** currp) {
if (!self->reachable) {
// convert to an unreachable. do this without UB, even though we have no destructors on AST nodes
- #define DELEGATE(CLASS_TO_VISIT) \
- { ExpressionManipulator::convert<CLASS_TO_VISIT, Unreachable>(static_cast<CLASS_TO_VISIT*>(*currp)); break; }
+ #define DELEGATE(CLASS_TO_VISIT) { \
+ self->typeUpdater.noteRecursiveRemoval(*currp); \
+ ExpressionManipulator::convert<CLASS_TO_VISIT, Unreachable>(static_cast<CLASS_TO_VISIT*>(*currp)); \
+ break; \
+ }
switch ((*currp)->_id) {
case Expression::Id::BlockId: DELEGATE(Block);
case Expression::Id::IfId: DELEGATE(If);
@@ -398,6 +418,12 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>>
}
}
+ void visitDrop(Drop* curr) {
+ if (isDead(curr->value)) {
+ replaceCurrent(curr->value);
+ }
+ }
+
void visitHost(Host* curr) {
handleCall(curr);
}
diff --git a/src/passes/MergeBlocks.cpp b/src/passes/MergeBlocks.cpp
index 494bf5a9e..37ada0174 100644
--- a/src/passes/MergeBlocks.cpp
+++ b/src/passes/MergeBlocks.cpp
@@ -169,6 +169,7 @@ static void optimizeBlock(Block* curr, Module* module) {
// we can do it!
// reuse the drop
drop->value = child->list.back();
+ drop->finalize();
child->list.back() = drop;
child->finalize();
curr->list[i] = child;
@@ -223,10 +224,17 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> {
if (auto* block = child->dynCast<Block>()) {
if (!block->name.is() && block->list.size() >= 2) {
child = block->list.back();
+ // we modified child )which is *&), which modifies curr, which might change its type
+ // (e.g. (drop (block i32 .. (unreachable)))
+ // the child was a block of i32, and is being replaced with an unreachable, so the
+ // parent will likely need to be unreachable too
+ auto oldType = curr->type;
+ ReFinalize().walk(curr);
if (outer == nullptr) {
// reuse the block, move it out
block->list.back() = curr;
- block->finalize(); // last block element was our input, and is now our output, which may differ TODO optimize
+ // we want the block outside to have the same type as curr had
+ block->finalize(oldType);
replaceCurrent(block);
return block;
} else {
@@ -264,7 +272,16 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> {
}
void visitSelect(Select* curr) {
- optimize(curr, curr->condition, optimize(curr, curr->ifFalse, optimize(curr, curr->ifTrue), &curr->ifTrue), &curr->ifTrue, &curr->ifFalse);
+ Block* outer = nullptr;
+ outer = optimize(curr, curr->ifTrue, outer);
+ if (EffectAnalyzer(getPassOptions(), curr->ifTrue).hasSideEffects()) return;
+ outer = optimize(curr, curr->ifFalse, outer);
+ if (EffectAnalyzer(getPassOptions(), curr->ifFalse).hasSideEffects()) return;
+ /* . */ optimize(curr, curr->condition, outer);
+ }
+
+ void visitDrop(Drop* curr) {
+ optimize(curr, curr->value);
}
void visitBreak(Break* curr) {
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp
index 50a7c5589..0e7c34564 100644
--- a/src/passes/OptimizeInstructions.cpp
+++ b/src/passes/OptimizeInstructions.cpp
@@ -27,6 +27,7 @@
#include <ast_utils.h>
#include <ast/cost.h>
#include <ast/properties.h>
+#include <ast/manipulation.h>
namespace wasm {
diff --git a/src/passes/Precompute.cpp b/src/passes/Precompute.cpp
index 37686de1a..e06103c11 100644
--- a/src/passes/Precompute.cpp
+++ b/src/passes/Precompute.cpp
@@ -23,6 +23,7 @@
#include <wasm-builder.h>
#include <wasm-interpreter.h>
#include <ast_utils.h>
+#include "ast/manipulation.h"
namespace wasm {
@@ -142,6 +143,11 @@ struct Precompute : public WalkerPass<PostWalker<Precompute, UnifiedExpressionVi
ExpressionManipulator::nop(curr);
}
}
+
+ void visitFunction(Function* curr) {
+ // removing breaks can alter types
+ ReFinalize().walkFunctionInModule(curr, getModule());
+ }
};
Pass *createPrecomputePass() {
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index 9a2edcb6b..a90cba7a7 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -25,6 +25,13 @@
namespace wasm {
+static int forceFull() {
+ if (getenv("BINARYEN_PRINT_FULL")) {
+ return std::stoi(getenv("BINARYEN_PRINT_FULL"));
+ }
+ return 0;
+}
+
struct PrintSExpression : public Visitor<PrintSExpression> {
std::ostream& o;
unsigned indent = 0;
@@ -41,9 +48,7 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
PrintSExpression(std::ostream& o) : o(o) {
setMinify(false);
- if (getenv("BINARYEN_PRINT_FULL")) {
- full = std::stoi(getenv("BINARYEN_PRINT_FULL"));
- }
+ if (!full) full = forceFull();
}
void visit(Expression* curr) {
@@ -802,7 +807,7 @@ std::ostream& WasmPrinter::printExpression(Expression* expression, std::ostream&
}
PrintSExpression print(o);
print.setMinify(minify);
- if (full) {
+ if (full || forceFull()) {
print.setFull(true);
o << "[" << printWasmType(expression->type) << "] ";
}
diff --git a/src/passes/RelooperJumpThreading.cpp b/src/passes/RelooperJumpThreading.cpp
index d3fd1844f..3656b5523 100644
--- a/src/passes/RelooperJumpThreading.cpp
+++ b/src/passes/RelooperJumpThreading.cpp
@@ -22,9 +22,11 @@
#include "wasm.h"
#include "pass.h"
#include "ast_utils.h"
+#include "ast/manipulation.h"
namespace wasm {
+
static Name LABEL("label");
static Name getInnerName(int i) {
@@ -147,6 +149,11 @@ struct RelooperJumpThreading : public WalkerPass<ExpressionStackWalker<RelooperJ
}
}
+ void visitFunction(Function* curr) {
+ // we may alter types
+ ReFinalize().walkFunctionInModule(curr, getModule());
+ }
+
private:
bool hasIrreducibleControlFlow(If* iff, Expression* origin) {
diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp
index 785b1c92e..4273b9f63 100644
--- a/src/passes/RemoveUnusedBrs.cpp
+++ b/src/passes/RemoveUnusedBrs.cpp
@@ -305,19 +305,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
if (worked) {
// Our work may alter block and if types, they may now return values that we made flow through them
- struct TypeUpdater : public WalkerPass<PostWalker<TypeUpdater>> {
- void visitBlock(Block* curr) {
- curr->finalize();
- }
- void visitLoop(Loop* curr) {
- curr->finalize();
- }
- void visitIf(If* curr) {
- curr->finalize();
- }
- };
- TypeUpdater typeUpdater;
- typeUpdater.walkFunction(func);
+ ReFinalize().walkFunctionInModule(func, getModule());
}
// thread trivial jumps
@@ -370,18 +358,22 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
}
}
- void finish() {
+ void finish(Function* func) {
for (auto& iter : newNames) {
auto* br = iter.first;
auto name = iter.second;
br->name = name;
}
+ if (newNames.size() > 0) {
+ // by changing where brs go, we may change block types etc.
+ ReFinalize().walkFunctionInModule(func, getModule());
+ }
}
};
JumpThreader jumpThreader;
jumpThreader.setModule(getModule());
jumpThreader.walkFunction(func);
- jumpThreader.finish();
+ jumpThreader.finish(func);
// perform some final optimizations
struct FinalOptimizer : public PostWalker<FinalOptimizer> {
@@ -461,6 +453,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
));
curr->name = Name();
ExpressionManipulator::nop(br);
+ curr->finalize(curr->type);
return;
}
}
diff --git a/src/passes/RemoveUnusedNames.cpp b/src/passes/RemoveUnusedNames.cpp
index f2c1f8525..39bf068df 100644
--- a/src/passes/RemoveUnusedNames.cpp
+++ b/src/passes/RemoveUnusedNames.cpp
@@ -72,6 +72,7 @@ struct RemoveUnusedNames : public WalkerPass<PostWalker<RemoveUnusedNames>> {
WASM_UNREACHABLE();
}
}
+ child->finalize(child->type);
replaceCurrent(child);
}
}
diff --git a/src/passes/SimplifyLocals.cpp b/src/passes/SimplifyLocals.cpp
index 6609cfe71..a9e9de34b 100644
--- a/src/passes/SimplifyLocals.cpp
+++ b/src/passes/SimplifyLocals.cpp
@@ -47,6 +47,7 @@
#include <pass.h>
#include <ast_utils.h>
#include <ast/count.h>
+#include <ast/manipulation.h>
namespace wasm {
diff --git a/src/passes/Vacuum.cpp b/src/passes/Vacuum.cpp
index 13b9c62fd..1f820ec9d 100644
--- a/src/passes/Vacuum.cpp
+++ b/src/passes/Vacuum.cpp
@@ -22,14 +22,17 @@
#include <pass.h>
#include <ast_utils.h>
#include <wasm-builder.h>
+#include <ast/block-utils.h>
namespace wasm {
-struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> {
+struct Vacuum : public WalkerPass<PostWalker<Vacuum>> {
bool isFunctionParallel() override { return true; }
Pass* create() override { return new Vacuum; }
+ bool needRefinalize = false;
+
// returns nullptr if curr is dead, curr if it must stay as is, or another node if it can be replaced
Expression* optimize(Expression* curr, bool resultUsed) {
while (1) {
@@ -164,46 +167,45 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> {
list.push_back(last);
}
needResize = false;
+ needRefinalize = true;
break;
}
}
}
if (needResize) {
list.resize(size - skip);
+ // resizing means we drop elements, which may include breaks, which may
+ // render blocks unreachable now
+ needRefinalize = true;
}
- if (!curr->name.is()) {
- if (list.size() == 1) {
- // just one element. replace the block, either with it or with a nop if it's not needed
- if (isConcreteWasmType(curr->type) || EffectAnalyzer(getPassOptions(), list[0]).hasSideEffects()) {
- replaceCurrent(list[0]);
- } else {
- if (curr->type == unreachable) {
- ExpressionManipulator::convert<Block, Unreachable>(curr);
- } else {
- ExpressionManipulator::nop(curr);
- }
- }
- } else if (list.size() == 0) {
- ExpressionManipulator::nop(curr);
- }
- }
+ // the block may now be a trivial one that we can get rid of and just leave its contents
+ replaceCurrent(BlockUtils::simplifyToContents(curr, this));
}
void visitIf(If* curr) {
// if the condition is a constant, just apply it
// we can just return the ifTrue or ifFalse.
if (auto* value = curr->condition->dynCast<Const>()) {
+ Expression* child;
if (value->value.getInteger()) {
- replaceCurrent(curr->ifTrue);
- return;
+ child = curr->ifTrue;
} else {
if (curr->ifFalse) {
- replaceCurrent(curr->ifFalse);
+ child = curr->ifFalse;
} else {
ExpressionManipulator::nop(curr);
+ return;
}
- return;
}
+ replaceCurrent(child);
+ if (curr->type != child->type) {
+ // e.g., if (1) unreachable is none => unreachable
+ // or if i32 (1) unreachable else 10 is i32 => unreachable
+ // in which cases we must update our parents.
+ // we must do this now, so that our parents see valid data
+ ReFinalize().walk(getFunction()->body);
+ }
+ return;
}
if (curr->ifFalse) {
if (curr->ifFalse->is<Nop>()) {
@@ -253,8 +255,10 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> {
// if we are dropping a block's return value, we might be able to remove it entirely
if (auto* block = curr->value->dynCast<Block>()) {
auto* last = block->list.back();
- if (isConcreteWasmType(last->type)) {
- assert(block->type == last->type);
+ // note that the last element may be concrete but not the block, if the
+ // block has an unreachable element in the middle, making the block unreachable
+ // despite later elements and in particular the last
+ if (isConcreteWasmType(last->type) && block->type == last->type) {
last = optimize(last, false);
if (!last) {
// we may be able to remove this, if there are no brs
@@ -303,6 +307,9 @@ struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> {
}
void visitFunction(Function* curr) {
+ if (needRefinalize) {
+ ReFinalize().walk(curr->body);
+ }
auto* optimized = optimize(curr->body, curr->result != none);
if (optimized) {
curr->body = optimized;
diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp
index 7b4a896eb..2fb0d5c3b 100644
--- a/src/passes/pass.cpp
+++ b/src/passes/pass.cpp
@@ -166,11 +166,7 @@ static void dumpWast(Name name, Module* wasm) {
}
void PassRunner::run() {
- // BINARYEN_PASS_DEBUG is a convenient commandline way to log out the toplevel passes, their times,
- // and validate between each pass.
- // (we don't recurse pass debug into sub-passes, as it doesn't help anyhow and
- // also is bad for e.g. printing which is a pass)
- static const int passDebug = getenv("BINARYEN_PASS_DEBUG") ? atoi(getenv("BINARYEN_PASS_DEBUG")) : 0;
+ static const int passDebug = getPassDebug();
if (!isNested && (options.debug || passDebug)) {
// for debug logging purposes, run each pass in full before running the other
auto totalTime = std::chrono::duration<double>(0);
@@ -212,7 +208,7 @@ void PassRunner::run() {
if (passDebug >= 2) {
std::cerr << "Last pass (" << pass->name << ") broke validation. Here is the module before: \n" << moduleBefore.str() << "\n";
} else {
- std::cerr << "Last pass (" << pass->name << ") broke validation. Run with BINARYEN_PASS_DEBUG=2 in the env to see the earlier state (FIXME: this is broken, need to prevent recursion of the print pass\n";
+ std::cerr << "Last pass (" << pass->name << ") broke validation. Run with BINARYEN_PASS_DEBUG=2 in the env to see the earlier state, or 3 to dump byn-* files for each pass\n";
}
abort();
}
@@ -300,4 +296,9 @@ void PassRunner::runPassOnFunction(Pass* pass, Function* func) {
instance->runFunction(this, wasm, func);
}
+int PassRunner::getPassDebug() {
+ static const int passDebug = getenv("BINARYEN_PASS_DEBUG") ? atoi(getenv("BINARYEN_PASS_DEBUG")) : 0;
+ return passDebug;
+}
+
} // namespace wasm
diff --git a/src/s2wasm.h b/src/s2wasm.h
index d0579a402..6ba4750ec 100644
--- a/src/s2wasm.h
+++ b/src/s2wasm.h
@@ -1185,6 +1185,7 @@ class S2WasmBuilder {
if (isConcreteWasmType(block->type) && block->list.size() == 0) {
// empty blocks that return a value are not valid, fix that up
block->list.push_back(allocator->alloc<Unreachable>());
+ block->finalize();
}
bstack.pop_back();
} else if (peek(".LBB")) {
@@ -1207,7 +1208,7 @@ class S2WasmBuilder {
} else if (match("end_loop")) {
auto* loop = bstack.back()->cast<Loop>();
bstack.pop_back();
- loop->body->finalize();
+ loop->body->cast<Block>()->finalize();
loop->finalize(loop->type);
} else if (match("br_table")) {
auto curr = allocator->alloc<Switch>();
@@ -1293,7 +1294,7 @@ class S2WasmBuilder {
bstack.pop_back(); // remove the base block for the function body
assert(bstack.empty());
assert(estack.empty());
- func->body->dynCast<Block>()->finalize();
+ func->body->cast<Block>()->finalize();
wasm->addFunction(func);
}
diff --git a/src/tools/asm2wasm.cpp b/src/tools/asm2wasm.cpp
index dd2b6a791..24cff6f09 100644
--- a/src/tools/asm2wasm.cpp
+++ b/src/tools/asm2wasm.cpp
@@ -24,6 +24,7 @@
#include "wasm-builder.h"
#include "wasm-printing.h"
#include "wasm-io.h"
+#include "wasm-validator.h"
#include "asm2wasm.h"
@@ -189,7 +190,11 @@ int main(int argc, const char *argv[]) {
}
}
- if (options.debug) std::cerr << "printing..." << std::endl;
+ if (!WasmValidator().validate(wasm)) {
+ Fatal() << "error in validating output";
+ }
+
+ if (options.debug) std::cerr << "emitting..." << std::endl;
ModuleWriter writer;
writer.setDebug(options.debug);
writer.setDebugInfo(passOptions.debugInfo);
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index 04e0aa4de..42d3dfe47 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -17,7 +17,8 @@
#ifndef wasm_wasm_builder_h
#define wasm_wasm_builder_h
-#include <wasm.h>
+#include "wasm.h"
+#include "ast/manipulation.h"
namespace wasm {
@@ -379,6 +380,25 @@ public:
std::swap(iff->ifTrue, iff->ifFalse);
iff->condition = makeUnary(EqZInt32, iff->condition);
}
+
+ // returns a replacement with the precise same type, and with
+ // minimal contents. as a replacement, this may reuse the
+ // input node
+ template<typename T>
+ Expression* replaceWithIdenticalType(T* curr) {
+ Literal value;
+ // TODO: reuse node conditionally when possible for literals
+ switch (curr->type) {
+ case i32: value = Literal(int32_t(0)); break;
+ case i64: value = Literal(int64_t(0)); break;
+ case f32: value = Literal(float(0)); break;
+ case f64: value = Literal(double(0)); break;
+ case none: return ExpressionManipulator::nop(curr);
+ case unreachable: return ExpressionManipulator::convert<T, Unreachable>(curr);
+ }
+ return makeConst(value);
+ }
+
};
} // namespace wasm
diff --git a/src/wasm-module-building.h b/src/wasm-module-building.h
index 023a2d020..b501a1ab6 100644
--- a/src/wasm-module-building.h
+++ b/src/wasm-module-building.h
@@ -94,6 +94,7 @@ public:
: wasm(wasm), numFunctions(numFunctions), passOptions(passOptions), addPrePasses(addPrePasses), endMarker(nullptr), list(nullptr), nextFunction(0),
numWorkers(0), liveWorkers(0), activeWorkers(0), availableFuncs(0), finishedFuncs(0),
finishing(false), debug(debug), validateGlobally(validateGlobally) {
+
if (!useWorkers()) {
// if we shouldn't use threads, don't
return;
@@ -136,7 +137,7 @@ public:
}
bool useWorkers() {
- return numFunctions > 0 && !debug && ThreadPool::getNumCores() > 1;
+ return numFunctions > 0 && !debug && ThreadPool::getNumCores() > 1 && !PassRunner::getPassDebug();
}
// Add a function to the module, and to be optimized
diff --git a/src/wasm-traversal.h b/src/wasm-traversal.h
index 522f5092c..f49407cad 100644
--- a/src/wasm-traversal.h
+++ b/src/wasm-traversal.h
@@ -157,7 +157,11 @@ struct Walker : public VisitorType {
// just one visit*() method is called by the traversal; if you replace a node,
// and you want to process the output, you must do that explicitly).
Expression* replaceCurrent(Expression* expression) {
- return replace = expression;
+ return *replacep = expression;
+ }
+
+ Expression* getCurrent() {
+ return *replacep;
}
// Get the current module
@@ -184,6 +188,15 @@ struct Walker : public VisitorType {
setFunction(nullptr);
}
+ void walkFunctionInModule(Function* func, Module* module) {
+ setModule(module);
+ setFunction(func);
+ static_cast<SubType*>(this)->doWalkFunction(func);
+ static_cast<SubType*>(this)->visitFunction(func);
+ setFunction(nullptr);
+ setModule(nullptr);
+ }
+
// override this to provide custom functionality
void doWalkFunction(Function* func) {
walk(func->body);
@@ -264,12 +277,9 @@ struct Walker : public VisitorType {
pushTask(SubType::scan, &root);
while (stack.size() > 0) {
auto task = popTask();
+ replacep = task.currp;
assert(*task.currp);
task.func(static_cast<SubType*>(this), task.currp);
- if (replace) {
- *task.currp = replace;
- replace = nullptr;
- }
}
}
@@ -311,7 +321,7 @@ struct Walker : public VisitorType {
}
private:
- Expression* replace = nullptr; // a node to replace
+ Expression** replacep = nullptr; // the address of the current node, used to replace it
std::vector<Task> stack; // stack of tasks
Function* currFunction = nullptr; // current function being processed
Module* currModule = nullptr; // current module being processed
@@ -571,6 +581,13 @@ struct ExpressionStackWalker : public PostWalker<SubType, VisitorType> {
self->pushTask(SubType::doPreVisit, currp);
}
+
+ Expression* replaceCurrent(Expression* expression) {
+ PostWalker<SubType, VisitorType>::replaceCurrent(expression);
+ // also update the stack
+ expressionStack.back() = expression;
+ return expression;
+ }
};
// Traversal in the order of execution. This is quick and simple, but
diff --git a/src/wasm-validator.h b/src/wasm-validator.h
index b657b67a6..66375790c 100644
--- a/src/wasm-validator.h
+++ b/src/wasm-validator.h
@@ -42,6 +42,7 @@
#include "support/colors.h"
#include "wasm.h"
#include "wasm-printing.h"
+#include "ast_utils.h"
namespace wasm {
@@ -68,7 +69,7 @@ struct WasmValidator : public PostWalker<WasmValidator> {
void noteLabelName(Name name) {
if (!name.is()) return;
- shouldBeTrue(labelNames.find(name) == labelNames.end(), name, "names in Binaren IR must be unique - IR generators must ensure that");
+ shouldBeTrue(labelNames.find(name) == labelNames.end(), name, "names in Binaryen IR must be unique - IR generators must ensure that");
labelNames.insert(name);
}
@@ -76,7 +77,13 @@ public:
bool validate(Module& module, bool validateWeb_ = false, bool validateGlobally_ = true) {
validateWeb = validateWeb_;
validateGlobally = validateGlobally_;
+ // wasm logic validation
walkModule(&module);
+ // validate additional internal IR details when in pass-debug mode
+ if (PassRunner::getPassDebug()) {
+ validateBinaryenIR(module);
+ }
+ // print if an error occurred
if (!valid) {
WasmPrinter::printModule(&module, std::cerr);
}
@@ -222,16 +229,23 @@ public:
}
}
void visitBreak(Break *curr) {
- noteBreak(curr->name, curr->value, curr);
+ // note breaks (that are actually taken)
+ if ((!curr->value || curr->value->type != unreachable) &&
+ (!curr->condition || curr->condition->type != unreachable)) {
+ noteBreak(curr->name, curr->value, curr);
+ }
if (curr->condition) {
shouldBeTrue(curr->condition->type == unreachable || curr->condition->type == i32, curr, "break condition must be i32");
}
}
void visitSwitch(Switch *curr) {
- for (auto& target : curr->targets) {
- noteBreak(target, curr->value, curr);
+ // note breaks (that are actually taken)
+ if (curr->condition->type != unreachable && (!curr->value || curr->value->type != unreachable)) {
+ for (auto& target : curr->targets) {
+ noteBreak(target, curr->value, curr);
+ }
+ noteBreak(curr->default_, curr->value, curr);
}
- noteBreak(curr->default_, curr->value, curr);
shouldBeTrue(curr->condition->type == unreachable || curr->condition->type == i32, curr, "br_table condition must be i32");
}
void visitCall(Call *curr) {
@@ -612,8 +626,6 @@ public:
PostWalker<WasmValidator>::doWalkFunction(func);
}
-private:
-
// helpers
std::ostream& fail() {
@@ -718,6 +730,37 @@ private:
default: {}
}
}
+
+ void validateBinaryenIR(Module& wasm) {
+ struct BinaryenIRValidator : public PostWalker<BinaryenIRValidator, UnifiedExpressionVisitor<BinaryenIRValidator>> {
+ WasmValidator& parent;
+
+ BinaryenIRValidator(WasmValidator& parent) : parent(parent) {}
+
+ void visitExpression(Expression* curr) {
+ // check if a node type is 'stale', i.e., we forgot to finalize() the node.
+ auto oldType = curr->type;
+ ReFinalizeNode().visit(curr);
+ auto newType = curr->type;
+ if (newType != oldType) {
+ // We accept concrete => undefined,
+ // e.g.
+ //
+ // (drop (block i32 (unreachable)))
+ //
+ // The block has an added type, not derived from the ast itself, so it is
+ // ok for it to be either i32 or unreachable.
+ if (!(isConcreteWasmType(oldType) && newType == unreachable)) {
+ parent.fail() << "stale type found in " << getFunction()->name << " on " << curr << "\n(marked as " << printWasmType(oldType) << ", should be " << printWasmType(newType) << ")\n";
+ parent.valid = false;
+ }
+ curr->type = oldType;
+ }
+ }
+ };
+ BinaryenIRValidator binaryenIRValidator(*this);
+ binaryenIRValidator.walkModule(&wasm);
+ }
};
} // namespace wasm
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index f9e3912f7..9b053d5b8 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -185,9 +185,11 @@ Element* SExpressionParser::parseString() {
input++;
std::string str;
while (1) {
+ if (input[0] == 0) throw ParseException("unterminated string", line, start - lineStart);
if (input[0] == '"') break;
if (input[0] == '\\') {
str += input[0];
+ if (input[1] == 0) throw ParseException("unterminated string escape", line, start - lineStart);
str += input[1];
input += 2;
continue;
diff --git a/test/dot_s/unreachable_blocks.wast b/test/dot_s/unreachable_blocks.wast
index f1593a00c..d9e921a4a 100644
--- a/test/dot_s/unreachable_blocks.wast
+++ b/test/dot_s/unreachable_blocks.wast
@@ -14,7 +14,7 @@
(return
(i32.const 2)
)
- (block $label$0 i32
+ (block $label$0
(unreachable)
)
)
@@ -22,7 +22,7 @@
(return
(i64.const 3)
)
- (block $label$0 i64
+ (block $label$0
(unreachable)
)
)
@@ -30,7 +30,7 @@
(return
(f32.const 4.5)
)
- (block $label$0 f32
+ (block $label$0
(unreachable)
)
)
@@ -38,7 +38,7 @@
(return
(f64.const 5.5)
)
- (block $label$0 f64
+ (block $label$0
(unreachable)
)
)
diff --git a/test/passes/O.txt b/test/passes/O.txt
index eac56294b..d560e326b 100644
--- a/test/passes/O.txt
+++ b/test/passes/O.txt
@@ -1,7 +1,9 @@
(module
(type $0 (func (result i32)))
+ (type $1 (func (param i64)))
(memory $0 0)
(export "ret" (func $ret))
+ (export "waka" (func $if-0-unreachable-to-none))
(func $ret (type $0) (result i32)
(block $out i32
(drop
@@ -16,4 +18,7 @@
(i32.const 999)
)
)
+ (func $if-0-unreachable-to-none (type $1) (param $0 i64)
+ (unreachable)
+ )
)
diff --git a/test/passes/O.wast b/test/passes/O.wast
index 8477c5306..8029c7b5b 100644
--- a/test/passes/O.wast
+++ b/test/passes/O.wast
@@ -13,5 +13,16 @@
(unreachable)
)
)
+ (func $if-0-unreachable-to-none (export "waka") (param $var$0 i64)
+ (local $var$1 i64)
+ (local $var$2 i64)
+ (block $label$1
+ (if
+ (i32.const 0)
+ (br $label$1)
+ (unreachable)
+ )
+ )
+ )
)
diff --git a/test/passes/coalesce-locals-learning.txt b/test/passes/coalesce-locals-learning.txt
index 08cb9a004..3bd04680d 100644
--- a/test/passes/coalesce-locals-learning.txt
+++ b/test/passes/coalesce-locals-learning.txt
@@ -110,7 +110,7 @@
(br $block)
(nop)
(drop
- (unreachable)
+ (i32.const 0)
)
(nop)
)
@@ -391,10 +391,10 @@
(block $block
(br $block)
(drop
- (unreachable)
+ (i32.const 0)
)
(drop
- (unreachable)
+ (i32.const 0)
)
)
)
@@ -403,10 +403,10 @@
(block $block
(unreachable)
(drop
- (unreachable)
+ (i32.const 0)
)
(drop
- (unreachable)
+ (i32.const 0)
)
)
)
@@ -415,10 +415,10 @@
(block $block
(return)
(drop
- (unreachable)
+ (i32.const 0)
)
(drop
- (unreachable)
+ (i32.const 0)
)
)
)
@@ -462,7 +462,7 @@
(i32.const 100)
)
(drop
- (unreachable)
+ (i32.const 0)
)
)
(drop
diff --git a/test/passes/coalesce-locals.txt b/test/passes/coalesce-locals.txt
index e022d59f9..381c76653 100644
--- a/test/passes/coalesce-locals.txt
+++ b/test/passes/coalesce-locals.txt
@@ -114,7 +114,7 @@
(br $block)
(nop)
(drop
- (unreachable)
+ (i32.const 0)
)
(nop)
)
@@ -393,10 +393,10 @@
(block $block
(br $block)
(drop
- (unreachable)
+ (i32.const 0)
)
(drop
- (unreachable)
+ (i32.const 0)
)
)
)
@@ -405,10 +405,10 @@
(block $block
(unreachable)
(drop
- (unreachable)
+ (i32.const 0)
)
(drop
- (unreachable)
+ (i32.const 0)
)
)
)
@@ -417,10 +417,10 @@
(block $block
(return)
(drop
- (unreachable)
+ (i32.const 0)
)
(drop
- (unreachable)
+ (i32.const 0)
)
)
)
@@ -464,7 +464,7 @@
(i32.const 100)
)
(drop
- (unreachable)
+ (i32.const 0)
)
)
(drop
@@ -904,7 +904,7 @@
(return)
(nop)
(drop
- (unreachable)
+ (i32.const 0)
)
(nop)
)
@@ -912,7 +912,7 @@
(unreachable)
(nop)
(drop
- (unreachable)
+ (i32.const 0)
)
(nop)
)
@@ -920,7 +920,7 @@
(br $z)
(nop)
(drop
- (unreachable)
+ (i32.const 0)
)
(nop)
)
@@ -930,7 +930,7 @@
)
(nop)
(drop
- (unreachable)
+ (i32.const 0)
)
(nop)
)
@@ -940,8 +940,8 @@
(block $block
(unreachable)
(i32.store
- (unreachable)
- (unreachable)
+ (i32.const 0)
+ (i32.const 0)
)
)
)
@@ -1028,7 +1028,7 @@
(loop $top
(if
(i32.const 1)
- (set_local $0
+ (tee_local $0
(unreachable)
)
)
diff --git a/test/passes/dce.txt b/test/passes/dce.txt
index 3ccdbdffd..62cee370f 100644
--- a/test/passes/dce.txt
+++ b/test/passes/dce.txt
@@ -47,21 +47,15 @@
)
(if
(i32.const 0)
- (drop
- (unreachable)
- )
+ (unreachable)
)
(if
(i32.const 0)
- (drop
- (unreachable)
- )
+ (unreachable)
)
(if
(i32.const 0)
- (drop
- (unreachable)
- )
+ (unreachable)
)
(block $out16
(block $in
@@ -199,9 +193,7 @@
)
(if
(i32.const 22)
- (drop
- (unreachable)
- )
+ (unreachable)
)
(if
(i32.const 33)
@@ -222,25 +214,19 @@
)
(if
(i32.const 66)
- (drop
- (unreachable)
- )
+ (unreachable)
)
(if
(i32.const 77)
- (drop
- (unreachable)
- )
+ (unreachable)
)
(if
(i32.const 88)
- (drop
- (block
- (drop
- (i32.const 0)
- )
- (unreachable)
+ (block
+ (drop
+ (i32.const 0)
)
+ (unreachable)
)
)
(if
@@ -249,34 +235,28 @@
)
(if
(i32.const 100)
- (drop
- (block
- (drop
- (i32.const 123)
- )
- (drop
- (i32.const 456)
- )
- (unreachable)
+ (block
+ (drop
+ (i32.const 123)
)
+ (drop
+ (i32.const 456)
+ )
+ (unreachable)
)
)
(if
(i32.const 101)
- (drop
- (block
- (drop
- (i32.const 123)
- )
- (unreachable)
+ (block
+ (drop
+ (i32.const 123)
)
+ (unreachable)
)
)
(if
(i32.const 102)
- (drop
- (unreachable)
- )
+ (unreachable)
)
(drop
(i32.const 1337)
@@ -313,7 +293,6 @@
(get_local $$$0)
)
)
- (unreachable)
)
(func $global (type $1)
(unreachable)
@@ -336,20 +315,14 @@
)
)
(func $unreachable-block-ends-switch (type $2) (result i32)
- (block $label$0 i32
- (block $label$3
- (nop)
- (unreachable)
- )
+ (block $label$3
+ (nop)
(unreachable)
)
)
(func $unreachable-block-ends-br_if (type $1) (result i32)
- (block $label$0 i32
- (block $label$2
- (nop)
- (unreachable)
- )
+ (block $label$2
+ (nop)
(unreachable)
)
)
@@ -364,13 +337,11 @@
(drop
(i32.const 1)
)
- (drop
- (block
- (drop
- (i32.const 4104)
- )
- (unreachable)
+ (block
+ (drop
+ (i32.const 4104)
)
+ (unreachable)
)
)
(func $call-unreach (type $4) (param $var$0 i64) (param $var$1 i64) (result i64)
@@ -404,4 +375,70 @@
)
)
)
+ (func $br-gone-means-block-type-changes-then-refinalize-at-end-is-too-late (type $1) (param $var$0 i32) (result i32)
+ (block $block
+ (nop)
+ (unreachable)
+ )
+ )
+ (func $br-with-unreachable-value-should-not-give-a-block-a-value (type $1) (param $var$0 i32) (result i32)
+ (block $label$0 i32
+ (block $block
+ (drop
+ (br_if $label$0
+ (i32.const 8)
+ (get_local $var$0)
+ )
+ )
+ (unreachable)
+ )
+ )
+ )
+ (func $replace-br-value-of-i32-with-unreachable (type $2) (result i32)
+ (block $label$1
+ (nop)
+ (unreachable)
+ )
+ )
+ (func $shorten-block-requires-sync-refinalize (type $ii) (param $var$0 i32) (param $var$1 i32)
+ (unreachable)
+ )
+ (func $block-with-type-but-is-unreachable (type $3) (param $var$0 i32) (result i32)
+ (block $block
+ (nop)
+ (unreachable)
+ )
+ )
+ (func $if-with-type-but-is-unreachable (type $3) (param $var$0 i32) (result i32)
+ (if
+ (get_local $var$0)
+ (unreachable)
+ (unreachable)
+ )
+ )
+ (func $unreachable-loop (type $1)
+ (unreachable)
+ )
+ (func $br-block-from-unary (type $2) (result i32)
+ (block $label$6 i32
+ (br $label$6
+ (i32.const 8)
+ )
+ )
+ )
+ (func $replace-unary-with-br-child (type $1)
+ (drop
+ (block $label$6 i32
+ (br $label$6
+ (i32.const 8)
+ )
+ )
+ )
+ )
+ (func $br_if-unreach-then-br_if-normal (type $1)
+ (block $out
+ (nop)
+ (unreachable)
+ )
+ )
)
diff --git a/test/passes/dce.wast b/test/passes/dce.wast
index 87a5757cd..7ee37e874 100644
--- a/test/passes/dce.wast
+++ b/test/passes/dce.wast
@@ -538,4 +538,117 @@
)
)
)
+ (func $br-gone-means-block-type-changes-then-refinalize-at-end-is-too-late (type $1) (param $var$0 i32) (result i32)
+ (block $label$0 i32
+ (br $label$0
+ (block i32
+ (nop)
+ (drop
+ (br_if $label$0
+ (unreachable)
+ (get_local $var$0)
+ )
+ )
+ (i32.const 4)
+ )
+ )
+ )
+ )
+ (func $br-with-unreachable-value-should-not-give-a-block-a-value (type $1) (param $var$0 i32) (result i32)
+ (block $label$0 i32
+ (br $label$0
+ (block i32 ;; turns into unreachable when refinalized
+ (drop
+ (br_if $label$0
+ (i32.const 8)
+ (get_local $var$0)
+ )
+ )
+ (unreachable)
+ )
+ )
+ (i32.const 16)
+ )
+ )
+ (func $replace-br-value-of-i32-with-unreachable (result i32)
+ (block $label$0 i32
+ (br $label$0
+ (block $label$1 i32
+ (nop)
+ (unreachable)
+ )
+ )
+ )
+ )
+ (func $shorten-block-requires-sync-refinalize (param $var$0 i32) (param $var$1 i32)
+ (block $label$0
+ (unreachable)
+ (if
+ (unreachable)
+ (br_if $label$0
+ (get_local $var$1)
+ )
+ )
+ )
+ )
+ (func $block-with-type-but-is-unreachable (param $var$0 i32) (result i32)
+ (block $label$0 i32
+ (br $label$0
+ (block $block i32
+ (nop)
+ (unreachable)
+ )
+ )
+ )
+ )
+ (func $if-with-type-but-is-unreachable (param $var$0 i32) (result i32)
+ (block $label$0 i32
+ (br $label$0
+ (if i32
+ (get_local $var$0)
+ (unreachable)
+ (unreachable)
+ )
+ )
+ )
+ )
+ (func $unreachable-loop
+ (loop $label$2
+ (unreachable)
+ (br $label$2)
+ )
+ )
+ (func $br-block-from-unary (result i32)
+ (block $label$6 i32
+ (i32.ctz
+ (block $label$7 i32
+ (br $label$6
+ (i32.const 8)
+ )
+ )
+ )
+ )
+ )
+ (func $replace-unary-with-br-child
+ (drop
+ (block $label$6 i32
+ (i32.ctz
+ (br $label$6
+ (i32.const 8)
+ )
+ )
+ )
+ )
+ )
+ (func $br_if-unreach-then-br_if-normal
+ (block $out
+ (nop)
+ (br_if $out
+ (unreachable)
+ )
+ (br_if $out
+ (i32.const 1)
+ )
+ )
+ )
)
diff --git a/test/passes/dce_vacuum.bin.txt b/test/passes/dce_vacuum.bin.txt
index 8d365b327..dc8138b24 100644
--- a/test/passes/dce_vacuum.bin.txt
+++ b/test/passes/dce_vacuum.bin.txt
@@ -5,7 +5,7 @@
(export "f32.compute_radix" (func $0))
(export "f64.compute_radix" (func $1))
(func $0 (type $0) (param $var$0 f32) (param $var$1 f32) (result f32)
- (block $label$0 f32
+ (block $label$0
(loop $label$1
(br_if $label$1
(f32.eq
@@ -28,24 +28,22 @@
)
)
)
- (drop
- (block
- (drop
- (call $0
- (f32.add
- (get_local $var$0)
- (tee_local $var$1
- (f32.add
- (get_local $var$1)
- (f32.const 1)
- )
+ (block
+ (drop
+ (call $0
+ (f32.add
+ (get_local $var$0)
+ (tee_local $var$1
+ (f32.add
+ (get_local $var$1)
+ (f32.const 1)
)
)
- (get_local $var$0)
)
+ (get_local $var$0)
)
- (unreachable)
)
+ (unreachable)
)
)
)
diff --git a/test/passes/dce_vacuum.txt b/test/passes/dce_vacuum.txt
index 0f1ec5095..ac61cce04 100644
--- a/test/passes/dce_vacuum.txt
+++ b/test/passes/dce_vacuum.txt
@@ -9,9 +9,7 @@
)
)
(func $drop-unreachable (type $1) (param $var$0 f32) (param $var$1 f32) (result f32)
- (drop
- (unreachable)
- )
+ (unreachable)
)
(func $set-unreachable (type $2) (param $var$0 i64) (result i64)
(local $var$1 i64)
diff --git a/test/passes/precompute.txt b/test/passes/precompute.txt
index acd96dee5..3de84aabe 100644
--- a/test/passes/precompute.txt
+++ b/test/passes/precompute.txt
@@ -105,4 +105,28 @@
(return)
)
)
+ (func $refinalize-br-condition-unreachable (type $2)
+ (block $label$1
+ (drop
+ (br_if $label$1
+ (unreachable)
+ )
+ )
+ )
+ )
+ (func $br_if-condition-is-block-i32-but-unreachable-so-refinalize-tricky (type $2)
+ (drop
+ (block $label$1
+ (drop
+ (br_if $label$1
+ (i32.const 100)
+ (block $label$3
+ (unreachable)
+ )
+ )
+ )
+ (i32.const 0)
+ )
+ )
+ )
)
diff --git a/test/passes/precompute.wast b/test/passes/precompute.wast
index d5e91fb9d..aafa8e947 100644
--- a/test/passes/precompute.wast
+++ b/test/passes/precompute.wast
@@ -186,4 +186,28 @@
(return)
)
)
+ (func $refinalize-br-condition-unreachable
+ (block $label$1
+ (drop
+ (br_if $label$1
+ (unreachable)
+ )
+ )
+ )
+ )
+ (func $br_if-condition-is-block-i32-but-unreachable-so-refinalize-tricky
+ (drop
+ (block $label$1 i32
+ (drop
+ (br_if $label$1
+ (i32.const 100)
+ (block $label$3 i32
+ (unreachable)
+ )
+ )
+ )
+ (i32.const 0)
+ )
+ )
+ )
)
diff --git a/test/passes/precompute_coalesce-locals_vacuum.txt b/test/passes/precompute_coalesce-locals_vacuum.txt
index 76b2303f2..fcf3cdf5d 100644
--- a/test/passes/precompute_coalesce-locals_vacuum.txt
+++ b/test/passes/precompute_coalesce-locals_vacuum.txt
@@ -2,9 +2,10 @@
(type $0 (func (param i32) (result i32)))
(memory $0 0)
(func $nested-br_if-value (type $0) (param $0 i32) (result i32)
- (loop $label$0 i32
- (block $block i32
+ (loop $label$0
+ (block $block
(br $label$0)
+ (i32.const 0)
)
)
)
diff --git a/test/passes/remove-unused-names_merge-blocks.txt b/test/passes/remove-unused-names_merge-blocks.txt
index 50f34c7b2..3c4d7a0a0 100644
--- a/test/passes/remove-unused-names_merge-blocks.txt
+++ b/test/passes/remove-unused-names_merge-blocks.txt
@@ -448,33 +448,27 @@
(drop
(i32.const 10)
)
+ (unreachable)
+ (drop
+ (i32.const 50)
+ )
(drop
(select
(i32.const 20)
- (block i32
- (unreachable)
- (i32.const 40)
- )
- (block i32
- (drop
- (i32.const 50)
- )
- (i32.const 60)
- )
+ (i32.const 40)
+ (i32.const 60)
)
)
(drop
(i32.const 10)
)
(drop
+ (i32.const 30)
+ )
+ (drop
(select
(i32.const 20)
- (block i32
- (drop
- (i32.const 30)
- )
- (unreachable)
- )
+ (unreachable)
(block i32
(drop
(i32.const 50)
@@ -489,14 +483,12 @@
(drop
(i32.const 30)
)
+ (unreachable)
(drop
(select
(i32.const 20)
(i32.const 40)
- (block i32
- (unreachable)
- (i32.const 60)
- )
+ (i32.const 60)
)
)
(drop
@@ -506,15 +498,13 @@
(i32.const 30)
)
(drop
+ (i32.const 50)
+ )
+ (drop
(select
(i32.const 20)
(i32.const 40)
- (block i32
- (drop
- (i32.const 50)
- )
- (unreachable)
- )
+ (unreachable)
)
)
)
@@ -751,4 +741,11 @@
)
(unreachable)
)
+ (func $drop-unreachable (type $4) (result i32)
+ (local $0 i32)
+ (drop
+ (unreachable)
+ )
+ (unreachable)
+ )
)
diff --git a/test/passes/remove-unused-names_merge-blocks.wast b/test/passes/remove-unused-names_merge-blocks.wast
index d41ff5fa4..adb27e6f0 100644
--- a/test/passes/remove-unused-names_merge-blocks.wast
+++ b/test/passes/remove-unused-names_merge-blocks.wast
@@ -914,4 +914,16 @@
)
(unreachable)
)
+
+ (func $drop-unreachable (result i32)
+ (local $0 i32)
+ (block $label$1 i32
+ (drop
+ (block i32
+ (unreachable)
+ )
+ )
+ (unreachable)
+ )
+ )
)
diff --git a/test/passes/remove-unused-names_precompute.txt b/test/passes/remove-unused-names_precompute.txt
new file mode 100644
index 000000000..dccd4708e
--- /dev/null
+++ b/test/passes/remove-unused-names_precompute.txt
@@ -0,0 +1,25 @@
+(module
+ (type $0 (func (param i32)))
+ (memory $0 256 256)
+ (func $__ZN10WasmAssertC2Ev__async_cb (type $0) (param $$0 i32)
+ (block $switch-default
+ (nop)
+ (block
+ (i32.store
+ (i32.const 12)
+ (i32.const 26)
+ )
+ (return)
+ )
+ )
+ (block
+ (set_local $$0
+ (i32.const 4)
+ )
+ (i32.store
+ (get_local $$0)
+ (i32.const 1)
+ )
+ )
+ )
+)
diff --git a/test/passes/remove-unused-names_precompute.wast b/test/passes/remove-unused-names_precompute.wast
new file mode 100644
index 000000000..5e1853389
--- /dev/null
+++ b/test/passes/remove-unused-names_precompute.wast
@@ -0,0 +1,28 @@
+(module
+ (memory $0 256 256)
+ (func $__ZN10WasmAssertC2Ev__async_cb (param $$0 i32)
+ (block $switch-default
+ (block $switch-case
+ (br_table $switch-case $switch-default
+ (i32.const 0)
+ )
+ )
+ (block
+ (i32.store
+ (i32.const 12)
+ (i32.const 26)
+ )
+ (return)
+ )
+ )
+ (block
+ (set_local $$0
+ (i32.const 4)
+ )
+ (i32.store
+ (get_local $$0)
+ (i32.const 1)
+ )
+ )
+ )
+)
diff --git a/test/passes/remove-unused-names_vacuum.txt b/test/passes/remove-unused-names_vacuum.txt
index 05112bf48..a1f49128e 100644
--- a/test/passes/remove-unused-names_vacuum.txt
+++ b/test/passes/remove-unused-names_vacuum.txt
@@ -1,5 +1,6 @@
(module
(type $0 (func (result i32)))
+ (type $1 (func))
(memory $0 0)
(func $return-i32-but-body-is-unreachable3 (type $0) (result i32)
(local $label i32)
@@ -10,4 +11,11 @@
(unreachable)
(i32.const 0)
)
+ (func $to-drop-unreachable (type $1)
+ (drop
+ (block i32
+ (unreachable)
+ )
+ )
+ )
)
diff --git a/test/passes/remove-unused-names_vacuum.wast b/test/passes/remove-unused-names_vacuum.wast
index 694d95cfd..cb5affac3 100644
--- a/test/passes/remove-unused-names_vacuum.wast
+++ b/test/passes/remove-unused-names_vacuum.wast
@@ -16,5 +16,12 @@
)
(i32.const 0)
)
+ (func $to-drop-unreachable
+ (drop
+ (block i32
+ (unreachable)
+ )
+ )
+ )
)
diff --git a/test/passes/rereloop_dce_remove-unused-brs_remove-unused-names_simplify-locals-nostructure_vacuum_reorder-locals_coalesce-locals_simplify-locals_reorder-locals_merge-blocks_remove-unused-brs_merge-blocks_vacuum.txt b/test/passes/rereloop_dce_remove-unused-brs_remove-unused-names_simplify-locals-nostructure_vacuum_reorder-locals_coalesce-locals_simplify-locals_reorder-locals_merge-blocks_remove-unused-brs_merge-blocks_vacuum.txt
index 5ed8e80c7..d41cd386e 100644
--- a/test/passes/rereloop_dce_remove-unused-brs_remove-unused-names_simplify-locals-nostructure_vacuum_reorder-locals_coalesce-locals_simplify-locals_reorder-locals_merge-blocks_remove-unused-brs_merge-blocks_vacuum.txt
+++ b/test/passes/rereloop_dce_remove-unused-brs_remove-unused-names_simplify-locals-nostructure_vacuum_reorder-locals_coalesce-locals_simplify-locals_reorder-locals_merge-blocks_remove-unused-brs_merge-blocks_vacuum.txt
@@ -46,14 +46,12 @@
(get_local $0)
)
)
- (block $block$11$break
- (loop $shape$9$continue
- (call $trivial)
- (br_if $shape$9$continue
+ (loop $shape$9$continue
+ (call $trivial)
+ (br_if $shape$9$continue
+ (i32.eqz
(i32.eqz
- (i32.eqz
- (get_local $0)
- )
+ (get_local $0)
)
)
)
diff --git a/test/passes/vacuum.txt b/test/passes/vacuum.txt
index f64ef0868..be18cc713 100644
--- a/test/passes/vacuum.txt
+++ b/test/passes/vacuum.txt
@@ -17,19 +17,11 @@
(set_local $x
(get_local $x)
)
- (block $in-a-block
- )
- (block $two-in-a-block
- )
(set_local $x
- (block $result-used i32
- (get_local $x)
- )
+ (get_local $x)
)
(set_local $x
- (block $two-and-result-used i32
- (get_local $y)
- )
+ (get_local $y)
)
)
(func $loopy (type $1) (param $0 i32)
@@ -98,20 +90,10 @@
)
)
(func $block-to-one (type $0)
- (block $block0
- )
- (block $block1
- (unreachable)
- )
- (block $block2
- (unreachable)
- )
- (block $block3
- (unreachable)
- )
- (block $block4
- (unreachable)
- )
+ (unreachable)
+ (unreachable)
+ (unreachable)
+ (unreachable)
)
(func $recurse (type $0)
(nop)
@@ -123,20 +105,18 @@
(if
(if i32
(get_local $d)
- (block $block1 i32
- (f64.ne
- (f64.promote/f32
- (f32.load
- (tee_local $l
- (i32.add
- (get_local $b)
- (i32.const 60)
- )
+ (f64.ne
+ (f64.promote/f32
+ (f32.load
+ (tee_local $l
+ (i32.add
+ (get_local $b)
+ (i32.const 60)
)
)
)
- (get_local $e)
)
+ (get_local $e)
)
(i32.const 0)
)
@@ -185,22 +165,7 @@
(func $relooperJumpThreading1 (type $0)
(local $$vararg_ptr5 i32)
(local $$11 i32)
- (loop $while-in$1
- (drop
- (block $jumpthreading$outer$8 i32
- (block $jumpthreading$inner$8
- (br $jumpthreading$outer$8
- (i32.const 0)
- )
- )
- (i32.store
- (get_local $$vararg_ptr5)
- (get_local $$11)
- )
- (i32.const 0)
- )
- )
- )
+ (nop)
)
(func $relooperJumpThreading2 (type $0)
(nop)
@@ -259,4 +224,36 @@
)
)
)
+ (func $if-1-block (type $1) (param $x i32)
+ (block $out
+ (if
+ (get_local $x)
+ (block $block9
+ (set_local $x
+ (get_local $x)
+ )
+ (br $out)
+ )
+ )
+ )
+ )
+ (func $block-resize-br-gone (type $0)
+ (block $out
+ (block $in
+ (call $block-resize-br-gone)
+ (br $in)
+ )
+ (return)
+ )
+ (block $out2
+ (block $in2
+ (br $in2)
+ )
+ (return)
+ )
+ )
+ (func $block-unreachable-but-last-element-concrete (type $0)
+ (local $2 i32)
+ (nop)
+ )
)
diff --git a/test/passes/vacuum.wast b/test/passes/vacuum.wast
index 8b97b9386..01c928e4d 100644
--- a/test/passes/vacuum.wast
+++ b/test/passes/vacuum.wast
@@ -463,4 +463,50 @@
)
)
)
+ (func $if-1-block (param $x i32)
+ (block $out
+ (if
+ (get_local $x)
+ (block
+ (if
+ (i32.const 1)
+ (block
+ (set_local $x
+ (get_local $x)
+ )
+ (br $out)
+ )
+ )
+ )
+ )
+ )
+ )
+ (func $block-resize-br-gone
+ (block $out
+ (block $in
+ (call $block-resize-br-gone)
+ (br $in)
+ (br $out)
+ )
+ (return)
+ )
+ (block $out2
+ (block $in2
+ (br $in2)
+ (br $out2)
+ )
+ (return)
+ )
+ )
+ (func $block-unreachable-but-last-element-concrete
+ (local $2 i32)
+ (block $label$0
+ (drop
+ (block
+ (br $label$0)
+ (get_local $2)
+ )
+ )
+ )
+ )
)
diff --git a/test/unreachable-import_wasm-only.fromasm b/test/unreachable-import_wasm-only.fromasm
index 642d266dc..fad6e4de8 100644
--- a/test/unreachable-import_wasm-only.fromasm
+++ b/test/unreachable-import_wasm-only.fromasm
@@ -8,19 +8,17 @@
(data (get_global $memoryBase) "unreachable-import_wasm-only.asm.js")
(export "__ZN10WasmAssertC2Ev__async_cb" (func $__ZN10WasmAssertC2Ev__async_cb))
(func $__ZN10WasmAssertC2Ev__async_cb (param $0 i32)
- (block $switch-default
- (i32.store
- (i32.const 12)
- (i32.const 26)
- )
- (return)
+ (i32.store
+ (i32.const 12)
+ (i32.const 26)
)
+ (return)
(i32.store
- (unreachable)
+ (i32.const 0)
(i32.const 1)
)
(call $___cxa_throw
- (unreachable)
+ (i32.const 0)
(i32.const 1280)
(i32.const 0)
)
diff --git a/test/unreachable-import_wasm-only.fromasm.clamp b/test/unreachable-import_wasm-only.fromasm.clamp
index 642d266dc..fad6e4de8 100644
--- a/test/unreachable-import_wasm-only.fromasm.clamp
+++ b/test/unreachable-import_wasm-only.fromasm.clamp
@@ -8,19 +8,17 @@
(data (get_global $memoryBase) "unreachable-import_wasm-only.asm.js")
(export "__ZN10WasmAssertC2Ev__async_cb" (func $__ZN10WasmAssertC2Ev__async_cb))
(func $__ZN10WasmAssertC2Ev__async_cb (param $0 i32)
- (block $switch-default
- (i32.store
- (i32.const 12)
- (i32.const 26)
- )
- (return)
+ (i32.store
+ (i32.const 12)
+ (i32.const 26)
)
+ (return)
(i32.store
- (unreachable)
+ (i32.const 0)
(i32.const 1)
)
(call $___cxa_throw
- (unreachable)
+ (i32.const 0)
(i32.const 1280)
(i32.const 0)
)
diff --git a/test/unreachable-import_wasm-only.fromasm.imprecise b/test/unreachable-import_wasm-only.fromasm.imprecise
index c6a954d24..a85dc9ed8 100644
--- a/test/unreachable-import_wasm-only.fromasm.imprecise
+++ b/test/unreachable-import_wasm-only.fromasm.imprecise
@@ -7,19 +7,17 @@
(import "env" "tableBase" (global $tableBase i32))
(export "__ZN10WasmAssertC2Ev__async_cb" (func $__ZN10WasmAssertC2Ev__async_cb))
(func $__ZN10WasmAssertC2Ev__async_cb (param $0 i32)
- (block $switch-default
- (i32.store
- (i32.const 12)
- (i32.const 26)
- )
- (return)
+ (i32.store
+ (i32.const 12)
+ (i32.const 26)
)
+ (return)
(i32.store
- (unreachable)
+ (i32.const 0)
(i32.const 1)
)
(call $___cxa_throw
- (unreachable)
+ (i32.const 0)
(i32.const 1280)
(i32.const 0)
)
diff --git a/test/wasm-only.asm.js b/test/wasm-only.asm.js
index 371197173..7287126d0 100644
--- a/test/wasm-only.asm.js
+++ b/test/wasm-only.asm.js
@@ -16,7 +16,10 @@ function asm(global, env, buffer) {
var HEAPF32 = new global.Float32Array(buffer);
var HEAPF64 = new global.Float64Array(buffer);
+ var STACKTOP = env.STACKTOP | 0;
+
var fround = global.Math.fround;
+ var Math_imul = global.Math.imul;
var illegalImport = env.illegalImport;
var illegalImportResult = env.illegalImportResult;
@@ -284,6 +287,135 @@ function asm(global, env, buffer) {
}
return 44;
}
+ function _memchr($src,$c,$n) {
+ $src = $src|0;
+ $c = $c|0;
+ $n = $n|0;
+ var $0 = 0, $1 = 0, $2 = 0, $3 = 0, $4 = 0, $5 = 0, $6 = 0, $7 = 0, $and = 0, $and15 = 0, $and16 = 0, $and39 = 0, $cmp = 0, $cmp11 = 0, $cmp1132 = 0, $cmp28 = 0, $cmp8 = 0, $cond = 0, $conv1 = 0, $dec = 0;
+ var $dec34 = 0, $incdec$ptr = 0, $incdec$ptr21 = 0, $incdec$ptr33 = 0, $lnot = 0, $mul = 0, $n$addr$0$lcssa = 0, $n$addr$0$lcssa52 = 0, $n$addr$043 = 0, $n$addr$1$lcssa = 0, $n$addr$133 = 0, $n$addr$227 = 0, $n$addr$3 = 0, $neg = 0, $or$cond = 0, $or$cond42 = 0, $s$0$lcssa = 0, $s$0$lcssa53 = 0, $s$044 = 0, $s$128 = 0;
+ var $s$2 = 0, $sub = 0, $sub22 = 0, $tobool = 0, $tobool2 = 0, $tobool2$lcssa = 0, $tobool241 = 0, $tobool25 = 0, $tobool2526 = 0, $tobool36 = 0, $tobool40 = 0, $w$0$lcssa = 0, $w$034 = 0, $xor = 0, label = 0, sp = 0;
+ sp = STACKTOP;
+ $conv1 = $c & 255;
+ $0 = $src;
+ $and39 = $0 & 3;
+ $tobool40 = ($and39|0)!=(0);
+ $tobool241 = ($n|0)!=(0);
+ $or$cond42 = $tobool241 & $tobool40;
+ L1: do {
+ if ($or$cond42) {
+ $1 = $c&255;
+ $n$addr$043 = $n;$s$044 = $src;
+ while(1) {
+ $2 = load1($s$044);
+ $cmp = ($2<<24>>24)==($1<<24>>24);
+ if ($cmp) {
+ $n$addr$0$lcssa52 = $n$addr$043;$s$0$lcssa53 = $s$044;
+ label = 6;
+ break L1;
+ }
+ $incdec$ptr = ((($s$044)) + 1|0);
+ $dec = (($n$addr$043) + -1)|0;
+ $3 = $incdec$ptr;
+ $and = $3 & 3;
+ $tobool = ($and|0)!=(0);
+ $tobool2 = ($dec|0)!=(0);
+ $or$cond = $tobool2 & $tobool;
+ if ($or$cond) {
+ $n$addr$043 = $dec;$s$044 = $incdec$ptr;
+ } else {
+ $n$addr$0$lcssa = $dec;$s$0$lcssa = $incdec$ptr;$tobool2$lcssa = $tobool2;
+ label = 5;
+ break;
+ }
+ }
+ } else {
+ $n$addr$0$lcssa = $n;$s$0$lcssa = $src;$tobool2$lcssa = $tobool241;
+ label = 5;
+ }
+ } while(0);
+ if ((label|0) == 5) {
+ if ($tobool2$lcssa) {
+ $n$addr$0$lcssa52 = $n$addr$0$lcssa;$s$0$lcssa53 = $s$0$lcssa;
+ label = 6;
+ } else {
+ $n$addr$3 = 0;$s$2 = $s$0$lcssa;
+ }
+ }
+ L8: do {
+ if ((label|0) == 6) {
+ $4 = load1($s$0$lcssa53);
+ $5 = $c&255;
+ $cmp8 = ($4<<24>>24)==($5<<24>>24);
+ if ($cmp8) {
+ $n$addr$3 = $n$addr$0$lcssa52;$s$2 = $s$0$lcssa53;
+ } else {
+ $mul = Math_imul($conv1, 16843009)|0;
+ $cmp1132 = ($n$addr$0$lcssa52>>>0)>(3);
+ L11: do {
+ if ($cmp1132) {
+ $n$addr$133 = $n$addr$0$lcssa52;$w$034 = $s$0$lcssa53;
+ while(1) {
+ $6 = load4($w$034);
+ $xor = $6 ^ $mul;
+ $sub = (($xor) + -16843009)|0;
+ $neg = $xor & -2139062144;
+ $and15 = $neg ^ -2139062144;
+ $and16 = $and15 & $sub;
+ $lnot = ($and16|0)==(0);
+ if (!($lnot)) {
+ break;
+ }
+ $incdec$ptr21 = ((($w$034)) + 4|0);
+ $sub22 = (($n$addr$133) + -4)|0;
+ $cmp11 = ($sub22>>>0)>(3);
+ if ($cmp11) {
+ $n$addr$133 = $sub22;$w$034 = $incdec$ptr21;
+ } else {
+ $n$addr$1$lcssa = $sub22;$w$0$lcssa = $incdec$ptr21;
+ label = 11;
+ break L11;
+ }
+ }
+ $n$addr$227 = $n$addr$133;$s$128 = $w$034;
+ } else {
+ $n$addr$1$lcssa = $n$addr$0$lcssa52;$w$0$lcssa = $s$0$lcssa53;
+ label = 11;
+ }
+ } while(0);
+ if ((label|0) == 11) {
+ $tobool2526 = ($n$addr$1$lcssa|0)==(0);
+ if ($tobool2526) {
+ $n$addr$3 = 0;$s$2 = $w$0$lcssa;
+ break;
+ } else {
+ $n$addr$227 = $n$addr$1$lcssa;$s$128 = $w$0$lcssa;
+ }
+ }
+ while(1) {
+ $7 = load1($s$128);
+ $cmp28 = ($7<<24>>24)==($5<<24>>24);
+ if ($cmp28) {
+ $n$addr$3 = $n$addr$227;$s$2 = $s$128;
+ break L8;
+ }
+ $incdec$ptr33 = ((($s$128)) + 1|0);
+ $dec34 = (($n$addr$227) + -1)|0;
+ $tobool25 = ($dec34|0)==(0);
+ if ($tobool25) {
+ $n$addr$3 = 0;$s$2 = $incdec$ptr33;
+ break;
+ } else {
+ $n$addr$227 = $dec34;$s$128 = $incdec$ptr33;
+ }
+ }
+ }
+ }
+ } while(0);
+ $tobool36 = ($n$addr$3|0)!=(0);
+ $cond = $tobool36 ? $s$2 : 0;
+ return ($cond|0);
+ }
+
function keepAlive() {
loads();
stores();
@@ -297,6 +429,8 @@ function asm(global, env, buffer) {
ifValue32(0, 0) | 0;
switch64(i64(0)) | 0;
unreachable_leftovers(0, 0, 0);
+ _memchr(0, 0, 0) | 0;
+ switch64TOOMUCH(i64(0)) | 0;
}
function __emscripten_dceable_type_decls() { // dce-able, but this defines the type of fabsf which has no other use
diff --git a/test/wasm-only.fromasm b/test/wasm-only.fromasm
index 6fa30ba12..3a98e84a3 100644
--- a/test/wasm-only.fromasm
+++ b/test/wasm-only.fromasm
@@ -365,6 +365,325 @@
)
)
)
+ (func $switch64TOOMUCH (param $0 i64) (result i32)
+ (local $1 i32)
+ (local $2 i64)
+ (block $switch-default
+ (if
+ (i64.ne
+ (tee_local $2
+ (get_local $0)
+ )
+ (i64.const -9223372036854775808)
+ )
+ (br_if $switch-default
+ (i64.ne
+ (get_local $2)
+ (i64.const 4611686018427387904)
+ )
+ )
+ )
+ (return
+ (i32.const 40)
+ )
+ )
+ (block $switch-default4
+ (if
+ (i32.ne
+ (tee_local $1
+ (i32.const 100)
+ )
+ (i32.const 214748364)
+ )
+ (br_if $switch-default4
+ (i32.ne
+ (get_local $1)
+ (i32.const 107374182)
+ )
+ )
+ )
+ (return
+ (i32.const 41)
+ )
+ )
+ (block $switch5
+ (if
+ (i64.ne
+ (get_local $0)
+ (i64.const -9223372036854775808)
+ )
+ (br_if $switch5
+ (i64.ne
+ (get_local $0)
+ (i64.const 4611686018427387904)
+ )
+ )
+ )
+ (return
+ (i32.const 42)
+ )
+ )
+ (block $switch8
+ (if
+ (i32.ne
+ (tee_local $1
+ (i32.const 100)
+ )
+ (i32.const 214748364)
+ )
+ (br_if $switch8
+ (i32.ne
+ (get_local $1)
+ (i32.const 107374182)
+ )
+ )
+ )
+ (return
+ (i32.const 43)
+ )
+ )
+ (i32.const 44)
+ )
+ (func $_memchr (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
+ (local $3 i32)
+ (local $4 i32)
+ (local $5 i32)
+ (set_local $5
+ (i32.and
+ (get_local $1)
+ (i32.const 255)
+ )
+ )
+ (block $label$break$L8
+ (block $__rjti$2
+ (if
+ (i32.and
+ (tee_local $4
+ (i32.ne
+ (get_local $2)
+ (i32.const 0)
+ )
+ )
+ (i32.ne
+ (i32.and
+ (get_local $0)
+ (i32.const 3)
+ )
+ (i32.const 0)
+ )
+ )
+ (block
+ (set_local $4
+ (i32.and
+ (get_local $1)
+ (i32.const 255)
+ )
+ )
+ (set_local $3
+ (get_local $2)
+ )
+ (set_local $2
+ (get_local $0)
+ )
+ (loop $while-in
+ (if
+ (i32.eq
+ (i32.load8_u
+ (get_local $2)
+ )
+ (i32.and
+ (get_local $4)
+ (i32.const 255)
+ )
+ )
+ (block
+ (set_local $0
+ (get_local $3)
+ )
+ (br $__rjti$2)
+ )
+ )
+ (br_if $while-in
+ (i32.and
+ (tee_local $0
+ (i32.ne
+ (tee_local $3
+ (i32.add
+ (get_local $3)
+ (i32.const -1)
+ )
+ )
+ (i32.const 0)
+ )
+ )
+ (i32.ne
+ (i32.and
+ (tee_local $2
+ (i32.add
+ (get_local $2)
+ (i32.const 1)
+ )
+ )
+ (i32.const 3)
+ )
+ (i32.const 0)
+ )
+ )
+ )
+ )
+ )
+ (block
+ (set_local $3
+ (get_local $2)
+ )
+ (set_local $2
+ (get_local $0)
+ )
+ (set_local $0
+ (get_local $4)
+ )
+ )
+ )
+ (if
+ (get_local $0)
+ (block
+ (set_local $0
+ (get_local $3)
+ )
+ (br $__rjti$2)
+ )
+ (set_local $0
+ (i32.const 0)
+ )
+ )
+ (br $label$break$L8)
+ )
+ (if
+ (i32.ne
+ (i32.load8_u
+ (get_local $2)
+ )
+ (tee_local $1
+ (i32.and
+ (get_local $1)
+ (i32.const 255)
+ )
+ )
+ )
+ (block
+ (set_local $3
+ (i32.mul
+ (get_local $5)
+ (i32.const 16843009)
+ )
+ )
+ (block $__rjto$0
+ (block $__rjti$0
+ (br_if $__rjti$0
+ (i32.le_u
+ (get_local $0)
+ (i32.const 3)
+ )
+ )
+ (loop $while-in3
+ (if
+ (i32.eqz
+ (i32.and
+ (i32.xor
+ (i32.and
+ (tee_local $4
+ (i32.xor
+ (i32.load
+ (get_local $2)
+ )
+ (get_local $3)
+ )
+ )
+ (i32.const -2139062144)
+ )
+ (i32.const -2139062144)
+ )
+ (i32.add
+ (get_local $4)
+ (i32.const -16843009)
+ )
+ )
+ )
+ (block
+ (set_local $2
+ (i32.add
+ (get_local $2)
+ (i32.const 4)
+ )
+ )
+ (br_if $while-in3
+ (i32.gt_u
+ (tee_local $0
+ (i32.add
+ (get_local $0)
+ (i32.const -4)
+ )
+ )
+ (i32.const 3)
+ )
+ )
+ (br $__rjti$0)
+ )
+ )
+ )
+ (br $__rjto$0)
+ )
+ (if
+ (i32.eqz
+ (get_local $0)
+ )
+ (block
+ (set_local $0
+ (i32.const 0)
+ )
+ (br $label$break$L8)
+ )
+ )
+ )
+ (loop $while-in5
+ (br_if $label$break$L8
+ (i32.eq
+ (i32.load8_u
+ (get_local $2)
+ )
+ (i32.and
+ (get_local $1)
+ (i32.const 255)
+ )
+ )
+ )
+ (set_local $2
+ (i32.add
+ (get_local $2)
+ (i32.const 1)
+ )
+ )
+ (br_if $while-in5
+ (tee_local $0
+ (i32.add
+ (get_local $0)
+ (i32.const -1)
+ )
+ )
+ )
+ (set_local $0
+ (i32.const 0)
+ )
+ )
+ )
+ )
+ )
+ (select
+ (get_local $2)
+ (i32.const 0)
+ (get_local $0)
+ )
+ )
(func $keepAlive
(call $loads)
(call $stores)
@@ -410,6 +729,18 @@
(i32.const 0)
(i32.const 0)
)
+ (drop
+ (call $_memchr
+ (i32.const 0)
+ (i32.const 0)
+ (i32.const 0)
+ )
+ )
+ (drop
+ (call $switch64TOOMUCH
+ (i64.const 0)
+ )
+ )
)
(func $legalstub$illegalParam (param $0 i32) (param $1 i32) (param $2 i32) (param $3 f64)
(call $illegalParam
diff --git a/test/wasm-only.fromasm.clamp b/test/wasm-only.fromasm.clamp
index 6fa30ba12..3a98e84a3 100644
--- a/test/wasm-only.fromasm.clamp
+++ b/test/wasm-only.fromasm.clamp
@@ -365,6 +365,325 @@
)
)
)
+ (func $switch64TOOMUCH (param $0 i64) (result i32)
+ (local $1 i32)
+ (local $2 i64)
+ (block $switch-default
+ (if
+ (i64.ne
+ (tee_local $2
+ (get_local $0)
+ )
+ (i64.const -9223372036854775808)
+ )
+ (br_if $switch-default
+ (i64.ne
+ (get_local $2)
+ (i64.const 4611686018427387904)
+ )
+ )
+ )
+ (return
+ (i32.const 40)
+ )
+ )
+ (block $switch-default4
+ (if
+ (i32.ne
+ (tee_local $1
+ (i32.const 100)
+ )
+ (i32.const 214748364)
+ )
+ (br_if $switch-default4
+ (i32.ne
+ (get_local $1)
+ (i32.const 107374182)
+ )
+ )
+ )
+ (return
+ (i32.const 41)
+ )
+ )
+ (block $switch5
+ (if
+ (i64.ne
+ (get_local $0)
+ (i64.const -9223372036854775808)
+ )
+ (br_if $switch5
+ (i64.ne
+ (get_local $0)
+ (i64.const 4611686018427387904)
+ )
+ )
+ )
+ (return
+ (i32.const 42)
+ )
+ )
+ (block $switch8
+ (if
+ (i32.ne
+ (tee_local $1
+ (i32.const 100)
+ )
+ (i32.const 214748364)
+ )
+ (br_if $switch8
+ (i32.ne
+ (get_local $1)
+ (i32.const 107374182)
+ )
+ )
+ )
+ (return
+ (i32.const 43)
+ )
+ )
+ (i32.const 44)
+ )
+ (func $_memchr (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
+ (local $3 i32)
+ (local $4 i32)
+ (local $5 i32)
+ (set_local $5
+ (i32.and
+ (get_local $1)
+ (i32.const 255)
+ )
+ )
+ (block $label$break$L8
+ (block $__rjti$2
+ (if
+ (i32.and
+ (tee_local $4
+ (i32.ne
+ (get_local $2)
+ (i32.const 0)
+ )
+ )
+ (i32.ne
+ (i32.and
+ (get_local $0)
+ (i32.const 3)
+ )
+ (i32.const 0)
+ )
+ )
+ (block
+ (set_local $4
+ (i32.and
+ (get_local $1)
+ (i32.const 255)
+ )
+ )
+ (set_local $3
+ (get_local $2)
+ )
+ (set_local $2
+ (get_local $0)
+ )
+ (loop $while-in
+ (if
+ (i32.eq
+ (i32.load8_u
+ (get_local $2)
+ )
+ (i32.and
+ (get_local $4)
+ (i32.const 255)
+ )
+ )
+ (block
+ (set_local $0
+ (get_local $3)
+ )
+ (br $__rjti$2)
+ )
+ )
+ (br_if $while-in
+ (i32.and
+ (tee_local $0
+ (i32.ne
+ (tee_local $3
+ (i32.add
+ (get_local $3)
+ (i32.const -1)
+ )
+ )
+ (i32.const 0)
+ )
+ )
+ (i32.ne
+ (i32.and
+ (tee_local $2
+ (i32.add
+ (get_local $2)
+ (i32.const 1)
+ )
+ )
+ (i32.const 3)
+ )
+ (i32.const 0)
+ )
+ )
+ )
+ )
+ )
+ (block
+ (set_local $3
+ (get_local $2)
+ )
+ (set_local $2
+ (get_local $0)
+ )
+ (set_local $0
+ (get_local $4)
+ )
+ )
+ )
+ (if
+ (get_local $0)
+ (block
+ (set_local $0
+ (get_local $3)
+ )
+ (br $__rjti$2)
+ )
+ (set_local $0
+ (i32.const 0)
+ )
+ )
+ (br $label$break$L8)
+ )
+ (if
+ (i32.ne
+ (i32.load8_u
+ (get_local $2)
+ )
+ (tee_local $1
+ (i32.and
+ (get_local $1)
+ (i32.const 255)
+ )
+ )
+ )
+ (block
+ (set_local $3
+ (i32.mul
+ (get_local $5)
+ (i32.const 16843009)
+ )
+ )
+ (block $__rjto$0
+ (block $__rjti$0
+ (br_if $__rjti$0
+ (i32.le_u
+ (get_local $0)
+ (i32.const 3)
+ )
+ )
+ (loop $while-in3
+ (if
+ (i32.eqz
+ (i32.and
+ (i32.xor
+ (i32.and
+ (tee_local $4
+ (i32.xor
+ (i32.load
+ (get_local $2)
+ )
+ (get_local $3)
+ )
+ )
+ (i32.const -2139062144)
+ )
+ (i32.const -2139062144)
+ )
+ (i32.add
+ (get_local $4)
+ (i32.const -16843009)
+ )
+ )
+ )
+ (block
+ (set_local $2
+ (i32.add
+ (get_local $2)
+ (i32.const 4)
+ )
+ )
+ (br_if $while-in3
+ (i32.gt_u
+ (tee_local $0
+ (i32.add
+ (get_local $0)
+ (i32.const -4)
+ )
+ )
+ (i32.const 3)
+ )
+ )
+ (br $__rjti$0)
+ )
+ )
+ )
+ (br $__rjto$0)
+ )
+ (if
+ (i32.eqz
+ (get_local $0)
+ )
+ (block
+ (set_local $0
+ (i32.const 0)
+ )
+ (br $label$break$L8)
+ )
+ )
+ )
+ (loop $while-in5
+ (br_if $label$break$L8
+ (i32.eq
+ (i32.load8_u
+ (get_local $2)
+ )
+ (i32.and
+ (get_local $1)
+ (i32.const 255)
+ )
+ )
+ )
+ (set_local $2
+ (i32.add
+ (get_local $2)
+ (i32.const 1)
+ )
+ )
+ (br_if $while-in5
+ (tee_local $0
+ (i32.add
+ (get_local $0)
+ (i32.const -1)
+ )
+ )
+ )
+ (set_local $0
+ (i32.const 0)
+ )
+ )
+ )
+ )
+ )
+ (select
+ (get_local $2)
+ (i32.const 0)
+ (get_local $0)
+ )
+ )
(func $keepAlive
(call $loads)
(call $stores)
@@ -410,6 +729,18 @@
(i32.const 0)
(i32.const 0)
)
+ (drop
+ (call $_memchr
+ (i32.const 0)
+ (i32.const 0)
+ (i32.const 0)
+ )
+ )
+ (drop
+ (call $switch64TOOMUCH
+ (i64.const 0)
+ )
+ )
)
(func $legalstub$illegalParam (param $0 i32) (param $1 i32) (param $2 i32) (param $3 f64)
(call $illegalParam
diff --git a/test/wasm-only.fromasm.clamp.no-opts b/test/wasm-only.fromasm.clamp.no-opts
index 3bcbf33d5..da52d1955 100644
--- a/test/wasm-only.fromasm.clamp.no-opts
+++ b/test/wasm-only.fromasm.clamp.no-opts
@@ -6,6 +6,7 @@
(type $legaltype$illegalImportResult (func (result i32)))
(type $legaltype$_fabsf (func (param f64) (result f64)))
(type $legaltype$do_i64 (func (result i32)))
+ (import "env" "STACKTOP" (global $STACKTOP$asm2wasm$import i32))
(import "env" "illegalImport" (func $illegalImport (param f64 i64 i32)))
(import "env" "illegalImportResult" (func $illegalImportResult (result i64)))
(import "env" "_fabsf" (func $_fabsf (param f32) (result f32)))
@@ -18,6 +19,7 @@
(import "env" "table" (table 3 3 anyfunc))
(import "env" "memoryBase" (global $memoryBase i32))
(import "env" "tableBase" (global $tableBase i32))
+ (global $STACKTOP (mut i32) (get_global $STACKTOP$asm2wasm$import))
(global $tempRet0 (mut i32) (i32.const 0))
(elem (get_global $tableBase) $legalfunc$illegalImport $legalfunc$_fabsf $legalfunc$do_i64)
(export "test64" (func $test64))
@@ -950,6 +952,584 @@
(i32.const 44)
)
)
+ (func $_memchr (param $$src i32) (param $$c i32) (param $$n i32) (result i32)
+ (local $$0 i32)
+ (local $$1 i32)
+ (local $$2 i32)
+ (local $$3 i32)
+ (local $$4 i32)
+ (local $$5 i32)
+ (local $$6 i32)
+ (local $$7 i32)
+ (local $$and i32)
+ (local $$and15 i32)
+ (local $$and16 i32)
+ (local $$and39 i32)
+ (local $$cmp i32)
+ (local $$cmp11 i32)
+ (local $$cmp1132 i32)
+ (local $$cmp28 i32)
+ (local $$cmp8 i32)
+ (local $$cond i32)
+ (local $$conv1 i32)
+ (local $$dec i32)
+ (local $$dec34 i32)
+ (local $$incdec$ptr i32)
+ (local $$incdec$ptr21 i32)
+ (local $$incdec$ptr33 i32)
+ (local $$lnot i32)
+ (local $$mul i32)
+ (local $$n$addr$0$lcssa i32)
+ (local $$n$addr$0$lcssa52 i32)
+ (local $$n$addr$043 i32)
+ (local $$n$addr$1$lcssa i32)
+ (local $$n$addr$133 i32)
+ (local $$n$addr$227 i32)
+ (local $$n$addr$3 i32)
+ (local $$neg i32)
+ (local $$or$cond i32)
+ (local $$or$cond42 i32)
+ (local $$s$0$lcssa i32)
+ (local $$s$0$lcssa53 i32)
+ (local $$s$044 i32)
+ (local $$s$128 i32)
+ (local $$s$2 i32)
+ (local $$sub i32)
+ (local $$sub22 i32)
+ (local $$tobool i32)
+ (local $$tobool2 i32)
+ (local $$tobool2$lcssa i32)
+ (local $$tobool241 i32)
+ (local $$tobool25 i32)
+ (local $$tobool2526 i32)
+ (local $$tobool36 i32)
+ (local $$tobool40 i32)
+ (local $$w$0$lcssa i32)
+ (local $$w$034 i32)
+ (local $$xor i32)
+ (local $label i32)
+ (local $sp i32)
+ (set_local $sp
+ (get_global $STACKTOP)
+ )
+ (set_local $$conv1
+ (i32.and
+ (get_local $$c)
+ (i32.const 255)
+ )
+ )
+ (set_local $$0
+ (get_local $$src)
+ )
+ (set_local $$and39
+ (i32.and
+ (get_local $$0)
+ (i32.const 3)
+ )
+ )
+ (set_local $$tobool40
+ (i32.ne
+ (get_local $$and39)
+ (i32.const 0)
+ )
+ )
+ (set_local $$tobool241
+ (i32.ne
+ (get_local $$n)
+ (i32.const 0)
+ )
+ )
+ (set_local $$or$cond42
+ (i32.and
+ (get_local $$tobool241)
+ (get_local $$tobool40)
+ )
+ )
+ (block $label$break$L1
+ (if
+ (get_local $$or$cond42)
+ (block
+ (set_local $$1
+ (i32.and
+ (get_local $$c)
+ (i32.const 255)
+ )
+ )
+ (set_local $$n$addr$043
+ (get_local $$n)
+ )
+ (set_local $$s$044
+ (get_local $$src)
+ )
+ (loop $while-in
+ (block $while-out
+ (set_local $$2
+ (i32.load8_s
+ (get_local $$s$044)
+ )
+ )
+ (set_local $$cmp
+ (i32.eq
+ (i32.shr_s
+ (i32.shl
+ (get_local $$2)
+ (i32.const 24)
+ )
+ (i32.const 24)
+ )
+ (i32.shr_s
+ (i32.shl
+ (get_local $$1)
+ (i32.const 24)
+ )
+ (i32.const 24)
+ )
+ )
+ )
+ (if
+ (get_local $$cmp)
+ (block
+ (set_local $$n$addr$0$lcssa52
+ (get_local $$n$addr$043)
+ )
+ (set_local $$s$0$lcssa53
+ (get_local $$s$044)
+ )
+ (set_local $label
+ (i32.const 6)
+ )
+ (br $label$break$L1)
+ )
+ )
+ (set_local $$incdec$ptr
+ (i32.add
+ (get_local $$s$044)
+ (i32.const 1)
+ )
+ )
+ (set_local $$dec
+ (i32.add
+ (get_local $$n$addr$043)
+ (i32.const -1)
+ )
+ )
+ (set_local $$3
+ (get_local $$incdec$ptr)
+ )
+ (set_local $$and
+ (i32.and
+ (get_local $$3)
+ (i32.const 3)
+ )
+ )
+ (set_local $$tobool
+ (i32.ne
+ (get_local $$and)
+ (i32.const 0)
+ )
+ )
+ (set_local $$tobool2
+ (i32.ne
+ (get_local $$dec)
+ (i32.const 0)
+ )
+ )
+ (set_local $$or$cond
+ (i32.and
+ (get_local $$tobool2)
+ (get_local $$tobool)
+ )
+ )
+ (if
+ (get_local $$or$cond)
+ (block
+ (set_local $$n$addr$043
+ (get_local $$dec)
+ )
+ (set_local $$s$044
+ (get_local $$incdec$ptr)
+ )
+ )
+ (block
+ (set_local $$n$addr$0$lcssa
+ (get_local $$dec)
+ )
+ (set_local $$s$0$lcssa
+ (get_local $$incdec$ptr)
+ )
+ (set_local $$tobool2$lcssa
+ (get_local $$tobool2)
+ )
+ (set_local $label
+ (i32.const 5)
+ )
+ (br $while-out)
+ )
+ )
+ (br $while-in)
+ )
+ )
+ )
+ (block
+ (set_local $$n$addr$0$lcssa
+ (get_local $$n)
+ )
+ (set_local $$s$0$lcssa
+ (get_local $$src)
+ )
+ (set_local $$tobool2$lcssa
+ (get_local $$tobool241)
+ )
+ (set_local $label
+ (i32.const 5)
+ )
+ )
+ )
+ )
+ (if
+ (i32.eq
+ (get_local $label)
+ (i32.const 5)
+ )
+ (if
+ (get_local $$tobool2$lcssa)
+ (block
+ (set_local $$n$addr$0$lcssa52
+ (get_local $$n$addr$0$lcssa)
+ )
+ (set_local $$s$0$lcssa53
+ (get_local $$s$0$lcssa)
+ )
+ (set_local $label
+ (i32.const 6)
+ )
+ )
+ (block
+ (set_local $$n$addr$3
+ (i32.const 0)
+ )
+ (set_local $$s$2
+ (get_local $$s$0$lcssa)
+ )
+ )
+ )
+ )
+ (block $label$break$L8
+ (if
+ (i32.eq
+ (get_local $label)
+ (i32.const 6)
+ )
+ (block
+ (set_local $$4
+ (i32.load8_s
+ (get_local $$s$0$lcssa53)
+ )
+ )
+ (set_local $$5
+ (i32.and
+ (get_local $$c)
+ (i32.const 255)
+ )
+ )
+ (set_local $$cmp8
+ (i32.eq
+ (i32.shr_s
+ (i32.shl
+ (get_local $$4)
+ (i32.const 24)
+ )
+ (i32.const 24)
+ )
+ (i32.shr_s
+ (i32.shl
+ (get_local $$5)
+ (i32.const 24)
+ )
+ (i32.const 24)
+ )
+ )
+ )
+ (if
+ (get_local $$cmp8)
+ (block
+ (set_local $$n$addr$3
+ (get_local $$n$addr$0$lcssa52)
+ )
+ (set_local $$s$2
+ (get_local $$s$0$lcssa53)
+ )
+ )
+ (block
+ (set_local $$mul
+ (i32.mul
+ (get_local $$conv1)
+ (i32.const 16843009)
+ )
+ )
+ (set_local $$cmp1132
+ (i32.gt_u
+ (get_local $$n$addr$0$lcssa52)
+ (i32.const 3)
+ )
+ )
+ (block $label$break$L11
+ (if
+ (get_local $$cmp1132)
+ (block
+ (set_local $$n$addr$133
+ (get_local $$n$addr$0$lcssa52)
+ )
+ (set_local $$w$034
+ (get_local $$s$0$lcssa53)
+ )
+ (loop $while-in3
+ (block $while-out2
+ (set_local $$6
+ (i32.load
+ (get_local $$w$034)
+ )
+ )
+ (set_local $$xor
+ (i32.xor
+ (get_local $$6)
+ (get_local $$mul)
+ )
+ )
+ (set_local $$sub
+ (i32.add
+ (get_local $$xor)
+ (i32.const -16843009)
+ )
+ )
+ (set_local $$neg
+ (i32.and
+ (get_local $$xor)
+ (i32.const -2139062144)
+ )
+ )
+ (set_local $$and15
+ (i32.xor
+ (get_local $$neg)
+ (i32.const -2139062144)
+ )
+ )
+ (set_local $$and16
+ (i32.and
+ (get_local $$and15)
+ (get_local $$sub)
+ )
+ )
+ (set_local $$lnot
+ (i32.eq
+ (get_local $$and16)
+ (i32.const 0)
+ )
+ )
+ (if
+ (i32.eqz
+ (get_local $$lnot)
+ )
+ (br $while-out2)
+ )
+ (set_local $$incdec$ptr21
+ (i32.add
+ (get_local $$w$034)
+ (i32.const 4)
+ )
+ )
+ (set_local $$sub22
+ (i32.add
+ (get_local $$n$addr$133)
+ (i32.const -4)
+ )
+ )
+ (set_local $$cmp11
+ (i32.gt_u
+ (get_local $$sub22)
+ (i32.const 3)
+ )
+ )
+ (if
+ (get_local $$cmp11)
+ (block
+ (set_local $$n$addr$133
+ (get_local $$sub22)
+ )
+ (set_local $$w$034
+ (get_local $$incdec$ptr21)
+ )
+ )
+ (block
+ (set_local $$n$addr$1$lcssa
+ (get_local $$sub22)
+ )
+ (set_local $$w$0$lcssa
+ (get_local $$incdec$ptr21)
+ )
+ (set_local $label
+ (i32.const 11)
+ )
+ (br $label$break$L11)
+ )
+ )
+ (br $while-in3)
+ )
+ )
+ (set_local $$n$addr$227
+ (get_local $$n$addr$133)
+ )
+ (set_local $$s$128
+ (get_local $$w$034)
+ )
+ )
+ (block
+ (set_local $$n$addr$1$lcssa
+ (get_local $$n$addr$0$lcssa52)
+ )
+ (set_local $$w$0$lcssa
+ (get_local $$s$0$lcssa53)
+ )
+ (set_local $label
+ (i32.const 11)
+ )
+ )
+ )
+ )
+ (if
+ (i32.eq
+ (get_local $label)
+ (i32.const 11)
+ )
+ (block
+ (set_local $$tobool2526
+ (i32.eq
+ (get_local $$n$addr$1$lcssa)
+ (i32.const 0)
+ )
+ )
+ (if
+ (get_local $$tobool2526)
+ (block
+ (set_local $$n$addr$3
+ (i32.const 0)
+ )
+ (set_local $$s$2
+ (get_local $$w$0$lcssa)
+ )
+ (br $label$break$L8)
+ )
+ (block
+ (set_local $$n$addr$227
+ (get_local $$n$addr$1$lcssa)
+ )
+ (set_local $$s$128
+ (get_local $$w$0$lcssa)
+ )
+ )
+ )
+ )
+ )
+ (loop $while-in5
+ (block $while-out4
+ (set_local $$7
+ (i32.load8_s
+ (get_local $$s$128)
+ )
+ )
+ (set_local $$cmp28
+ (i32.eq
+ (i32.shr_s
+ (i32.shl
+ (get_local $$7)
+ (i32.const 24)
+ )
+ (i32.const 24)
+ )
+ (i32.shr_s
+ (i32.shl
+ (get_local $$5)
+ (i32.const 24)
+ )
+ (i32.const 24)
+ )
+ )
+ )
+ (if
+ (get_local $$cmp28)
+ (block
+ (set_local $$n$addr$3
+ (get_local $$n$addr$227)
+ )
+ (set_local $$s$2
+ (get_local $$s$128)
+ )
+ (br $label$break$L8)
+ )
+ )
+ (set_local $$incdec$ptr33
+ (i32.add
+ (get_local $$s$128)
+ (i32.const 1)
+ )
+ )
+ (set_local $$dec34
+ (i32.add
+ (get_local $$n$addr$227)
+ (i32.const -1)
+ )
+ )
+ (set_local $$tobool25
+ (i32.eq
+ (get_local $$dec34)
+ (i32.const 0)
+ )
+ )
+ (if
+ (get_local $$tobool25)
+ (block
+ (set_local $$n$addr$3
+ (i32.const 0)
+ )
+ (set_local $$s$2
+ (get_local $$incdec$ptr33)
+ )
+ (br $while-out4)
+ )
+ (block
+ (set_local $$n$addr$227
+ (get_local $$dec34)
+ )
+ (set_local $$s$128
+ (get_local $$incdec$ptr33)
+ )
+ )
+ )
+ (br $while-in5)
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ (set_local $$tobool36
+ (i32.ne
+ (get_local $$n$addr$3)
+ (i32.const 0)
+ )
+ )
+ (set_local $$cond
+ (if i32
+ (get_local $$tobool36)
+ (get_local $$s$2)
+ (i32.const 0)
+ )
+ )
+ (return
+ (get_local $$cond)
+ )
+ )
(func $keepAlive
(call $loads)
(call $stores)
@@ -995,6 +1575,18 @@
(i32.const 0)
(i32.const 0)
)
+ (drop
+ (call $_memchr
+ (i32.const 0)
+ (i32.const 0)
+ (i32.const 0)
+ )
+ )
+ (drop
+ (call $switch64TOOMUCH
+ (i64.const 0)
+ )
+ )
)
(func $__emscripten_dceable_type_decls
(drop
diff --git a/test/wasm-only.fromasm.imprecise b/test/wasm-only.fromasm.imprecise
index 11eedc71d..de241740e 100644
--- a/test/wasm-only.fromasm.imprecise
+++ b/test/wasm-only.fromasm.imprecise
@@ -280,6 +280,325 @@
)
)
)
+ (func $switch64TOOMUCH (param $0 i64) (result i32)
+ (local $1 i32)
+ (local $2 i64)
+ (block $switch-default
+ (if
+ (i64.ne
+ (tee_local $2
+ (get_local $0)
+ )
+ (i64.const -9223372036854775808)
+ )
+ (br_if $switch-default
+ (i64.ne
+ (get_local $2)
+ (i64.const 4611686018427387904)
+ )
+ )
+ )
+ (return
+ (i32.const 40)
+ )
+ )
+ (block $switch-default4
+ (if
+ (i32.ne
+ (tee_local $1
+ (i32.const 100)
+ )
+ (i32.const 214748364)
+ )
+ (br_if $switch-default4
+ (i32.ne
+ (get_local $1)
+ (i32.const 107374182)
+ )
+ )
+ )
+ (return
+ (i32.const 41)
+ )
+ )
+ (block $switch5
+ (if
+ (i64.ne
+ (get_local $0)
+ (i64.const -9223372036854775808)
+ )
+ (br_if $switch5
+ (i64.ne
+ (get_local $0)
+ (i64.const 4611686018427387904)
+ )
+ )
+ )
+ (return
+ (i32.const 42)
+ )
+ )
+ (block $switch8
+ (if
+ (i32.ne
+ (tee_local $1
+ (i32.const 100)
+ )
+ (i32.const 214748364)
+ )
+ (br_if $switch8
+ (i32.ne
+ (get_local $1)
+ (i32.const 107374182)
+ )
+ )
+ )
+ (return
+ (i32.const 43)
+ )
+ )
+ (i32.const 44)
+ )
+ (func $_memchr (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
+ (local $3 i32)
+ (local $4 i32)
+ (local $5 i32)
+ (set_local $5
+ (i32.and
+ (get_local $1)
+ (i32.const 255)
+ )
+ )
+ (block $label$break$L8
+ (block $__rjti$2
+ (if
+ (i32.and
+ (tee_local $4
+ (i32.ne
+ (get_local $2)
+ (i32.const 0)
+ )
+ )
+ (i32.ne
+ (i32.and
+ (get_local $0)
+ (i32.const 3)
+ )
+ (i32.const 0)
+ )
+ )
+ (block
+ (set_local $4
+ (i32.and
+ (get_local $1)
+ (i32.const 255)
+ )
+ )
+ (set_local $3
+ (get_local $2)
+ )
+ (set_local $2
+ (get_local $0)
+ )
+ (loop $while-in
+ (if
+ (i32.eq
+ (i32.load8_u
+ (get_local $2)
+ )
+ (i32.and
+ (get_local $4)
+ (i32.const 255)
+ )
+ )
+ (block
+ (set_local $0
+ (get_local $3)
+ )
+ (br $__rjti$2)
+ )
+ )
+ (br_if $while-in
+ (i32.and
+ (tee_local $0
+ (i32.ne
+ (tee_local $3
+ (i32.add
+ (get_local $3)
+ (i32.const -1)
+ )
+ )
+ (i32.const 0)
+ )
+ )
+ (i32.ne
+ (i32.and
+ (tee_local $2
+ (i32.add
+ (get_local $2)
+ (i32.const 1)
+ )
+ )
+ (i32.const 3)
+ )
+ (i32.const 0)
+ )
+ )
+ )
+ )
+ )
+ (block
+ (set_local $3
+ (get_local $2)
+ )
+ (set_local $2
+ (get_local $0)
+ )
+ (set_local $0
+ (get_local $4)
+ )
+ )
+ )
+ (if
+ (get_local $0)
+ (block
+ (set_local $0
+ (get_local $3)
+ )
+ (br $__rjti$2)
+ )
+ (set_local $0
+ (i32.const 0)
+ )
+ )
+ (br $label$break$L8)
+ )
+ (if
+ (i32.ne
+ (i32.load8_u
+ (get_local $2)
+ )
+ (tee_local $1
+ (i32.and
+ (get_local $1)
+ (i32.const 255)
+ )
+ )
+ )
+ (block
+ (set_local $3
+ (i32.mul
+ (get_local $5)
+ (i32.const 16843009)
+ )
+ )
+ (block $__rjto$0
+ (block $__rjti$0
+ (br_if $__rjti$0
+ (i32.le_u
+ (get_local $0)
+ (i32.const 3)
+ )
+ )
+ (loop $while-in3
+ (if
+ (i32.eqz
+ (i32.and
+ (i32.xor
+ (i32.and
+ (tee_local $4
+ (i32.xor
+ (i32.load
+ (get_local $2)
+ )
+ (get_local $3)
+ )
+ )
+ (i32.const -2139062144)
+ )
+ (i32.const -2139062144)
+ )
+ (i32.add
+ (get_local $4)
+ (i32.const -16843009)
+ )
+ )
+ )
+ (block
+ (set_local $2
+ (i32.add
+ (get_local $2)
+ (i32.const 4)
+ )
+ )
+ (br_if $while-in3
+ (i32.gt_u
+ (tee_local $0
+ (i32.add
+ (get_local $0)
+ (i32.const -4)
+ )
+ )
+ (i32.const 3)
+ )
+ )
+ (br $__rjti$0)
+ )
+ )
+ )
+ (br $__rjto$0)
+ )
+ (if
+ (i32.eqz
+ (get_local $0)
+ )
+ (block
+ (set_local $0
+ (i32.const 0)
+ )
+ (br $label$break$L8)
+ )
+ )
+ )
+ (loop $while-in5
+ (br_if $label$break$L8
+ (i32.eq
+ (i32.load8_u
+ (get_local $2)
+ )
+ (i32.and
+ (get_local $1)
+ (i32.const 255)
+ )
+ )
+ )
+ (set_local $2
+ (i32.add
+ (get_local $2)
+ (i32.const 1)
+ )
+ )
+ (br_if $while-in5
+ (tee_local $0
+ (i32.add
+ (get_local $0)
+ (i32.const -1)
+ )
+ )
+ )
+ (set_local $0
+ (i32.const 0)
+ )
+ )
+ )
+ )
+ )
+ (select
+ (get_local $2)
+ (i32.const 0)
+ (get_local $0)
+ )
+ )
(func $keepAlive
(call $loads)
(call $stores)
@@ -325,6 +644,18 @@
(i32.const 0)
(i32.const 0)
)
+ (drop
+ (call $_memchr
+ (i32.const 0)
+ (i32.const 0)
+ (i32.const 0)
+ )
+ )
+ (drop
+ (call $switch64TOOMUCH
+ (i64.const 0)
+ )
+ )
)
(func $legalstub$illegalParam (param $0 i32) (param $1 i32) (param $2 i32) (param $3 f64)
(call $illegalParam
diff --git a/test/wasm-only.fromasm.imprecise.no-opts b/test/wasm-only.fromasm.imprecise.no-opts
index 2ab7a85ca..5bdb5bc00 100644
--- a/test/wasm-only.fromasm.imprecise.no-opts
+++ b/test/wasm-only.fromasm.imprecise.no-opts
@@ -6,6 +6,7 @@
(type $legaltype$illegalImportResult (func (result i32)))
(type $legaltype$_fabsf (func (param f64) (result f64)))
(type $legaltype$do_i64 (func (result i32)))
+ (import "env" "STACKTOP" (global $STACKTOP$asm2wasm$import i32))
(import "env" "illegalImport" (func $illegalImport (param f64 i64 i32)))
(import "env" "illegalImportResult" (func $illegalImportResult (result i64)))
(import "env" "_fabsf" (func $_fabsf (param f32) (result f32)))
@@ -18,6 +19,7 @@
(import "env" "table" (table 3 3 anyfunc))
(import "env" "memoryBase" (global $memoryBase i32))
(import "env" "tableBase" (global $tableBase i32))
+ (global $STACKTOP (mut i32) (get_global $STACKTOP$asm2wasm$import))
(global $tempRet0 (mut i32) (i32.const 0))
(elem (get_global $tableBase) $legalfunc$illegalImport $legalfunc$_fabsf $legalfunc$do_i64)
(export "test64" (func $test64))
@@ -889,6 +891,584 @@
(i32.const 44)
)
)
+ (func $_memchr (param $$src i32) (param $$c i32) (param $$n i32) (result i32)
+ (local $$0 i32)
+ (local $$1 i32)
+ (local $$2 i32)
+ (local $$3 i32)
+ (local $$4 i32)
+ (local $$5 i32)
+ (local $$6 i32)
+ (local $$7 i32)
+ (local $$and i32)
+ (local $$and15 i32)
+ (local $$and16 i32)
+ (local $$and39 i32)
+ (local $$cmp i32)
+ (local $$cmp11 i32)
+ (local $$cmp1132 i32)
+ (local $$cmp28 i32)
+ (local $$cmp8 i32)
+ (local $$cond i32)
+ (local $$conv1 i32)
+ (local $$dec i32)
+ (local $$dec34 i32)
+ (local $$incdec$ptr i32)
+ (local $$incdec$ptr21 i32)
+ (local $$incdec$ptr33 i32)
+ (local $$lnot i32)
+ (local $$mul i32)
+ (local $$n$addr$0$lcssa i32)
+ (local $$n$addr$0$lcssa52 i32)
+ (local $$n$addr$043 i32)
+ (local $$n$addr$1$lcssa i32)
+ (local $$n$addr$133 i32)
+ (local $$n$addr$227 i32)
+ (local $$n$addr$3 i32)
+ (local $$neg i32)
+ (local $$or$cond i32)
+ (local $$or$cond42 i32)
+ (local $$s$0$lcssa i32)
+ (local $$s$0$lcssa53 i32)
+ (local $$s$044 i32)
+ (local $$s$128 i32)
+ (local $$s$2 i32)
+ (local $$sub i32)
+ (local $$sub22 i32)
+ (local $$tobool i32)
+ (local $$tobool2 i32)
+ (local $$tobool2$lcssa i32)
+ (local $$tobool241 i32)
+ (local $$tobool25 i32)
+ (local $$tobool2526 i32)
+ (local $$tobool36 i32)
+ (local $$tobool40 i32)
+ (local $$w$0$lcssa i32)
+ (local $$w$034 i32)
+ (local $$xor i32)
+ (local $label i32)
+ (local $sp i32)
+ (set_local $sp
+ (get_global $STACKTOP)
+ )
+ (set_local $$conv1
+ (i32.and
+ (get_local $$c)
+ (i32.const 255)
+ )
+ )
+ (set_local $$0
+ (get_local $$src)
+ )
+ (set_local $$and39
+ (i32.and
+ (get_local $$0)
+ (i32.const 3)
+ )
+ )
+ (set_local $$tobool40
+ (i32.ne
+ (get_local $$and39)
+ (i32.const 0)
+ )
+ )
+ (set_local $$tobool241
+ (i32.ne
+ (get_local $$n)
+ (i32.const 0)
+ )
+ )
+ (set_local $$or$cond42
+ (i32.and
+ (get_local $$tobool241)
+ (get_local $$tobool40)
+ )
+ )
+ (block $label$break$L1
+ (if
+ (get_local $$or$cond42)
+ (block
+ (set_local $$1
+ (i32.and
+ (get_local $$c)
+ (i32.const 255)
+ )
+ )
+ (set_local $$n$addr$043
+ (get_local $$n)
+ )
+ (set_local $$s$044
+ (get_local $$src)
+ )
+ (loop $while-in
+ (block $while-out
+ (set_local $$2
+ (i32.load8_s
+ (get_local $$s$044)
+ )
+ )
+ (set_local $$cmp
+ (i32.eq
+ (i32.shr_s
+ (i32.shl
+ (get_local $$2)
+ (i32.const 24)
+ )
+ (i32.const 24)
+ )
+ (i32.shr_s
+ (i32.shl
+ (get_local $$1)
+ (i32.const 24)
+ )
+ (i32.const 24)
+ )
+ )
+ )
+ (if
+ (get_local $$cmp)
+ (block
+ (set_local $$n$addr$0$lcssa52
+ (get_local $$n$addr$043)
+ )
+ (set_local $$s$0$lcssa53
+ (get_local $$s$044)
+ )
+ (set_local $label
+ (i32.const 6)
+ )
+ (br $label$break$L1)
+ )
+ )
+ (set_local $$incdec$ptr
+ (i32.add
+ (get_local $$s$044)
+ (i32.const 1)
+ )
+ )
+ (set_local $$dec
+ (i32.add
+ (get_local $$n$addr$043)
+ (i32.const -1)
+ )
+ )
+ (set_local $$3
+ (get_local $$incdec$ptr)
+ )
+ (set_local $$and
+ (i32.and
+ (get_local $$3)
+ (i32.const 3)
+ )
+ )
+ (set_local $$tobool
+ (i32.ne
+ (get_local $$and)
+ (i32.const 0)
+ )
+ )
+ (set_local $$tobool2
+ (i32.ne
+ (get_local $$dec)
+ (i32.const 0)
+ )
+ )
+ (set_local $$or$cond
+ (i32.and
+ (get_local $$tobool2)
+ (get_local $$tobool)
+ )
+ )
+ (if
+ (get_local $$or$cond)
+ (block
+ (set_local $$n$addr$043
+ (get_local $$dec)
+ )
+ (set_local $$s$044
+ (get_local $$incdec$ptr)
+ )
+ )
+ (block
+ (set_local $$n$addr$0$lcssa
+ (get_local $$dec)
+ )
+ (set_local $$s$0$lcssa
+ (get_local $$incdec$ptr)
+ )
+ (set_local $$tobool2$lcssa
+ (get_local $$tobool2)
+ )
+ (set_local $label
+ (i32.const 5)
+ )
+ (br $while-out)
+ )
+ )
+ (br $while-in)
+ )
+ )
+ )
+ (block
+ (set_local $$n$addr$0$lcssa
+ (get_local $$n)
+ )
+ (set_local $$s$0$lcssa
+ (get_local $$src)
+ )
+ (set_local $$tobool2$lcssa
+ (get_local $$tobool241)
+ )
+ (set_local $label
+ (i32.const 5)
+ )
+ )
+ )
+ )
+ (if
+ (i32.eq
+ (get_local $label)
+ (i32.const 5)
+ )
+ (if
+ (get_local $$tobool2$lcssa)
+ (block
+ (set_local $$n$addr$0$lcssa52
+ (get_local $$n$addr$0$lcssa)
+ )
+ (set_local $$s$0$lcssa53
+ (get_local $$s$0$lcssa)
+ )
+ (set_local $label
+ (i32.const 6)
+ )
+ )
+ (block
+ (set_local $$n$addr$3
+ (i32.const 0)
+ )
+ (set_local $$s$2
+ (get_local $$s$0$lcssa)
+ )
+ )
+ )
+ )
+ (block $label$break$L8
+ (if
+ (i32.eq
+ (get_local $label)
+ (i32.const 6)
+ )
+ (block
+ (set_local $$4
+ (i32.load8_s
+ (get_local $$s$0$lcssa53)
+ )
+ )
+ (set_local $$5
+ (i32.and
+ (get_local $$c)
+ (i32.const 255)
+ )
+ )
+ (set_local $$cmp8
+ (i32.eq
+ (i32.shr_s
+ (i32.shl
+ (get_local $$4)
+ (i32.const 24)
+ )
+ (i32.const 24)
+ )
+ (i32.shr_s
+ (i32.shl
+ (get_local $$5)
+ (i32.const 24)
+ )
+ (i32.const 24)
+ )
+ )
+ )
+ (if
+ (get_local $$cmp8)
+ (block
+ (set_local $$n$addr$3
+ (get_local $$n$addr$0$lcssa52)
+ )
+ (set_local $$s$2
+ (get_local $$s$0$lcssa53)
+ )
+ )
+ (block
+ (set_local $$mul
+ (i32.mul
+ (get_local $$conv1)
+ (i32.const 16843009)
+ )
+ )
+ (set_local $$cmp1132
+ (i32.gt_u
+ (get_local $$n$addr$0$lcssa52)
+ (i32.const 3)
+ )
+ )
+ (block $label$break$L11
+ (if
+ (get_local $$cmp1132)
+ (block
+ (set_local $$n$addr$133
+ (get_local $$n$addr$0$lcssa52)
+ )
+ (set_local $$w$034
+ (get_local $$s$0$lcssa53)
+ )
+ (loop $while-in3
+ (block $while-out2
+ (set_local $$6
+ (i32.load
+ (get_local $$w$034)
+ )
+ )
+ (set_local $$xor
+ (i32.xor
+ (get_local $$6)
+ (get_local $$mul)
+ )
+ )
+ (set_local $$sub
+ (i32.add
+ (get_local $$xor)
+ (i32.const -16843009)
+ )
+ )
+ (set_local $$neg
+ (i32.and
+ (get_local $$xor)
+ (i32.const -2139062144)
+ )
+ )
+ (set_local $$and15
+ (i32.xor
+ (get_local $$neg)
+ (i32.const -2139062144)
+ )
+ )
+ (set_local $$and16
+ (i32.and
+ (get_local $$and15)
+ (get_local $$sub)
+ )
+ )
+ (set_local $$lnot
+ (i32.eq
+ (get_local $$and16)
+ (i32.const 0)
+ )
+ )
+ (if
+ (i32.eqz
+ (get_local $$lnot)
+ )
+ (br $while-out2)
+ )
+ (set_local $$incdec$ptr21
+ (i32.add
+ (get_local $$w$034)
+ (i32.const 4)
+ )
+ )
+ (set_local $$sub22
+ (i32.add
+ (get_local $$n$addr$133)
+ (i32.const -4)
+ )
+ )
+ (set_local $$cmp11
+ (i32.gt_u
+ (get_local $$sub22)
+ (i32.const 3)
+ )
+ )
+ (if
+ (get_local $$cmp11)
+ (block
+ (set_local $$n$addr$133
+ (get_local $$sub22)
+ )
+ (set_local $$w$034
+ (get_local $$incdec$ptr21)
+ )
+ )
+ (block
+ (set_local $$n$addr$1$lcssa
+ (get_local $$sub22)
+ )
+ (set_local $$w$0$lcssa
+ (get_local $$incdec$ptr21)
+ )
+ (set_local $label
+ (i32.const 11)
+ )
+ (br $label$break$L11)
+ )
+ )
+ (br $while-in3)
+ )
+ )
+ (set_local $$n$addr$227
+ (get_local $$n$addr$133)
+ )
+ (set_local $$s$128
+ (get_local $$w$034)
+ )
+ )
+ (block
+ (set_local $$n$addr$1$lcssa
+ (get_local $$n$addr$0$lcssa52)
+ )
+ (set_local $$w$0$lcssa
+ (get_local $$s$0$lcssa53)
+ )
+ (set_local $label
+ (i32.const 11)
+ )
+ )
+ )
+ )
+ (if
+ (i32.eq
+ (get_local $label)
+ (i32.const 11)
+ )
+ (block
+ (set_local $$tobool2526
+ (i32.eq
+ (get_local $$n$addr$1$lcssa)
+ (i32.const 0)
+ )
+ )
+ (if
+ (get_local $$tobool2526)
+ (block
+ (set_local $$n$addr$3
+ (i32.const 0)
+ )
+ (set_local $$s$2
+ (get_local $$w$0$lcssa)
+ )
+ (br $label$break$L8)
+ )
+ (block
+ (set_local $$n$addr$227
+ (get_local $$n$addr$1$lcssa)
+ )
+ (set_local $$s$128
+ (get_local $$w$0$lcssa)
+ )
+ )
+ )
+ )
+ )
+ (loop $while-in5
+ (block $while-out4
+ (set_local $$7
+ (i32.load8_s
+ (get_local $$s$128)
+ )
+ )
+ (set_local $$cmp28
+ (i32.eq
+ (i32.shr_s
+ (i32.shl
+ (get_local $$7)
+ (i32.const 24)
+ )
+ (i32.const 24)
+ )
+ (i32.shr_s
+ (i32.shl
+ (get_local $$5)
+ (i32.const 24)
+ )
+ (i32.const 24)
+ )
+ )
+ )
+ (if
+ (get_local $$cmp28)
+ (block
+ (set_local $$n$addr$3
+ (get_local $$n$addr$227)
+ )
+ (set_local $$s$2
+ (get_local $$s$128)
+ )
+ (br $label$break$L8)
+ )
+ )
+ (set_local $$incdec$ptr33
+ (i32.add
+ (get_local $$s$128)
+ (i32.const 1)
+ )
+ )
+ (set_local $$dec34
+ (i32.add
+ (get_local $$n$addr$227)
+ (i32.const -1)
+ )
+ )
+ (set_local $$tobool25
+ (i32.eq
+ (get_local $$dec34)
+ (i32.const 0)
+ )
+ )
+ (if
+ (get_local $$tobool25)
+ (block
+ (set_local $$n$addr$3
+ (i32.const 0)
+ )
+ (set_local $$s$2
+ (get_local $$incdec$ptr33)
+ )
+ (br $while-out4)
+ )
+ (block
+ (set_local $$n$addr$227
+ (get_local $$dec34)
+ )
+ (set_local $$s$128
+ (get_local $$incdec$ptr33)
+ )
+ )
+ )
+ (br $while-in5)
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ (set_local $$tobool36
+ (i32.ne
+ (get_local $$n$addr$3)
+ (i32.const 0)
+ )
+ )
+ (set_local $$cond
+ (if i32
+ (get_local $$tobool36)
+ (get_local $$s$2)
+ (i32.const 0)
+ )
+ )
+ (return
+ (get_local $$cond)
+ )
+ )
(func $keepAlive
(call $loads)
(call $stores)
@@ -934,6 +1514,18 @@
(i32.const 0)
(i32.const 0)
)
+ (drop
+ (call $_memchr
+ (i32.const 0)
+ (i32.const 0)
+ (i32.const 0)
+ )
+ )
+ (drop
+ (call $switch64TOOMUCH
+ (i64.const 0)
+ )
+ )
)
(func $__emscripten_dceable_type_decls
(drop
diff --git a/test/wasm-only.fromasm.no-opts b/test/wasm-only.fromasm.no-opts
index 3bcbf33d5..da52d1955 100644
--- a/test/wasm-only.fromasm.no-opts
+++ b/test/wasm-only.fromasm.no-opts
@@ -6,6 +6,7 @@
(type $legaltype$illegalImportResult (func (result i32)))
(type $legaltype$_fabsf (func (param f64) (result f64)))
(type $legaltype$do_i64 (func (result i32)))
+ (import "env" "STACKTOP" (global $STACKTOP$asm2wasm$import i32))
(import "env" "illegalImport" (func $illegalImport (param f64 i64 i32)))
(import "env" "illegalImportResult" (func $illegalImportResult (result i64)))
(import "env" "_fabsf" (func $_fabsf (param f32) (result f32)))
@@ -18,6 +19,7 @@
(import "env" "table" (table 3 3 anyfunc))
(import "env" "memoryBase" (global $memoryBase i32))
(import "env" "tableBase" (global $tableBase i32))
+ (global $STACKTOP (mut i32) (get_global $STACKTOP$asm2wasm$import))
(global $tempRet0 (mut i32) (i32.const 0))
(elem (get_global $tableBase) $legalfunc$illegalImport $legalfunc$_fabsf $legalfunc$do_i64)
(export "test64" (func $test64))
@@ -950,6 +952,584 @@
(i32.const 44)
)
)
+ (func $_memchr (param $$src i32) (param $$c i32) (param $$n i32) (result i32)
+ (local $$0 i32)
+ (local $$1 i32)
+ (local $$2 i32)
+ (local $$3 i32)
+ (local $$4 i32)
+ (local $$5 i32)
+ (local $$6 i32)
+ (local $$7 i32)
+ (local $$and i32)
+ (local $$and15 i32)
+ (local $$and16 i32)
+ (local $$and39 i32)
+ (local $$cmp i32)
+ (local $$cmp11 i32)
+ (local $$cmp1132 i32)
+ (local $$cmp28 i32)
+ (local $$cmp8 i32)
+ (local $$cond i32)
+ (local $$conv1 i32)
+ (local $$dec i32)
+ (local $$dec34 i32)
+ (local $$incdec$ptr i32)
+ (local $$incdec$ptr21 i32)
+ (local $$incdec$ptr33 i32)
+ (local $$lnot i32)
+ (local $$mul i32)
+ (local $$n$addr$0$lcssa i32)
+ (local $$n$addr$0$lcssa52 i32)
+ (local $$n$addr$043 i32)
+ (local $$n$addr$1$lcssa i32)
+ (local $$n$addr$133 i32)
+ (local $$n$addr$227 i32)
+ (local $$n$addr$3 i32)
+ (local $$neg i32)
+ (local $$or$cond i32)
+ (local $$or$cond42 i32)
+ (local $$s$0$lcssa i32)
+ (local $$s$0$lcssa53 i32)
+ (local $$s$044 i32)
+ (local $$s$128 i32)
+ (local $$s$2 i32)
+ (local $$sub i32)
+ (local $$sub22 i32)
+ (local $$tobool i32)
+ (local $$tobool2 i32)
+ (local $$tobool2$lcssa i32)
+ (local $$tobool241 i32)
+ (local $$tobool25 i32)
+ (local $$tobool2526 i32)
+ (local $$tobool36 i32)
+ (local $$tobool40 i32)
+ (local $$w$0$lcssa i32)
+ (local $$w$034 i32)
+ (local $$xor i32)
+ (local $label i32)
+ (local $sp i32)
+ (set_local $sp
+ (get_global $STACKTOP)
+ )
+ (set_local $$conv1
+ (i32.and
+ (get_local $$c)
+ (i32.const 255)
+ )
+ )
+ (set_local $$0
+ (get_local $$src)
+ )
+ (set_local $$and39
+ (i32.and
+ (get_local $$0)
+ (i32.const 3)
+ )
+ )
+ (set_local $$tobool40
+ (i32.ne
+ (get_local $$and39)
+ (i32.const 0)
+ )
+ )
+ (set_local $$tobool241
+ (i32.ne
+ (get_local $$n)
+ (i32.const 0)
+ )
+ )
+ (set_local $$or$cond42
+ (i32.and
+ (get_local $$tobool241)
+ (get_local $$tobool40)
+ )
+ )
+ (block $label$break$L1
+ (if
+ (get_local $$or$cond42)
+ (block
+ (set_local $$1
+ (i32.and
+ (get_local $$c)
+ (i32.const 255)
+ )
+ )
+ (set_local $$n$addr$043
+ (get_local $$n)
+ )
+ (set_local $$s$044
+ (get_local $$src)
+ )
+ (loop $while-in
+ (block $while-out
+ (set_local $$2
+ (i32.load8_s
+ (get_local $$s$044)
+ )
+ )
+ (set_local $$cmp
+ (i32.eq
+ (i32.shr_s
+ (i32.shl
+ (get_local $$2)
+ (i32.const 24)
+ )
+ (i32.const 24)
+ )
+ (i32.shr_s
+ (i32.shl
+ (get_local $$1)
+ (i32.const 24)
+ )
+ (i32.const 24)
+ )
+ )
+ )
+ (if
+ (get_local $$cmp)
+ (block
+ (set_local $$n$addr$0$lcssa52
+ (get_local $$n$addr$043)
+ )
+ (set_local $$s$0$lcssa53
+ (get_local $$s$044)
+ )
+ (set_local $label
+ (i32.const 6)
+ )
+ (br $label$break$L1)
+ )
+ )
+ (set_local $$incdec$ptr
+ (i32.add
+ (get_local $$s$044)
+ (i32.const 1)
+ )
+ )
+ (set_local $$dec
+ (i32.add
+ (get_local $$n$addr$043)
+ (i32.const -1)
+ )
+ )
+ (set_local $$3
+ (get_local $$incdec$ptr)
+ )
+ (set_local $$and
+ (i32.and
+ (get_local $$3)
+ (i32.const 3)
+ )
+ )
+ (set_local $$tobool
+ (i32.ne
+ (get_local $$and)
+ (i32.const 0)
+ )
+ )
+ (set_local $$tobool2
+ (i32.ne
+ (get_local $$dec)
+ (i32.const 0)
+ )
+ )
+ (set_local $$or$cond
+ (i32.and
+ (get_local $$tobool2)
+ (get_local $$tobool)
+ )
+ )
+ (if
+ (get_local $$or$cond)
+ (block
+ (set_local $$n$addr$043
+ (get_local $$dec)
+ )
+ (set_local $$s$044
+ (get_local $$incdec$ptr)
+ )
+ )
+ (block
+ (set_local $$n$addr$0$lcssa
+ (get_local $$dec)
+ )
+ (set_local $$s$0$lcssa
+ (get_local $$incdec$ptr)
+ )
+ (set_local $$tobool2$lcssa
+ (get_local $$tobool2)
+ )
+ (set_local $label
+ (i32.const 5)
+ )
+ (br $while-out)
+ )
+ )
+ (br $while-in)
+ )
+ )
+ )
+ (block
+ (set_local $$n$addr$0$lcssa
+ (get_local $$n)
+ )
+ (set_local $$s$0$lcssa
+ (get_local $$src)
+ )
+ (set_local $$tobool2$lcssa
+ (get_local $$tobool241)
+ )
+ (set_local $label
+ (i32.const 5)
+ )
+ )
+ )
+ )
+ (if
+ (i32.eq
+ (get_local $label)
+ (i32.const 5)
+ )
+ (if
+ (get_local $$tobool2$lcssa)
+ (block
+ (set_local $$n$addr$0$lcssa52
+ (get_local $$n$addr$0$lcssa)
+ )
+ (set_local $$s$0$lcssa53
+ (get_local $$s$0$lcssa)
+ )
+ (set_local $label
+ (i32.const 6)
+ )
+ )
+ (block
+ (set_local $$n$addr$3
+ (i32.const 0)
+ )
+ (set_local $$s$2
+ (get_local $$s$0$lcssa)
+ )
+ )
+ )
+ )
+ (block $label$break$L8
+ (if
+ (i32.eq
+ (get_local $label)
+ (i32.const 6)
+ )
+ (block
+ (set_local $$4
+ (i32.load8_s
+ (get_local $$s$0$lcssa53)
+ )
+ )
+ (set_local $$5
+ (i32.and
+ (get_local $$c)
+ (i32.const 255)
+ )
+ )
+ (set_local $$cmp8
+ (i32.eq
+ (i32.shr_s
+ (i32.shl
+ (get_local $$4)
+ (i32.const 24)
+ )
+ (i32.const 24)
+ )
+ (i32.shr_s
+ (i32.shl
+ (get_local $$5)
+ (i32.const 24)
+ )
+ (i32.const 24)
+ )
+ )
+ )
+ (if
+ (get_local $$cmp8)
+ (block
+ (set_local $$n$addr$3
+ (get_local $$n$addr$0$lcssa52)
+ )
+ (set_local $$s$2
+ (get_local $$s$0$lcssa53)
+ )
+ )
+ (block
+ (set_local $$mul
+ (i32.mul
+ (get_local $$conv1)
+ (i32.const 16843009)
+ )
+ )
+ (set_local $$cmp1132
+ (i32.gt_u
+ (get_local $$n$addr$0$lcssa52)
+ (i32.const 3)
+ )
+ )
+ (block $label$break$L11
+ (if
+ (get_local $$cmp1132)
+ (block
+ (set_local $$n$addr$133
+ (get_local $$n$addr$0$lcssa52)
+ )
+ (set_local $$w$034
+ (get_local $$s$0$lcssa53)
+ )
+ (loop $while-in3
+ (block $while-out2
+ (set_local $$6
+ (i32.load
+ (get_local $$w$034)
+ )
+ )
+ (set_local $$xor
+ (i32.xor
+ (get_local $$6)
+ (get_local $$mul)
+ )
+ )
+ (set_local $$sub
+ (i32.add
+ (get_local $$xor)
+ (i32.const -16843009)
+ )
+ )
+ (set_local $$neg
+ (i32.and
+ (get_local $$xor)
+ (i32.const -2139062144)
+ )
+ )
+ (set_local $$and15
+ (i32.xor
+ (get_local $$neg)
+ (i32.const -2139062144)
+ )
+ )
+ (set_local $$and16
+ (i32.and
+ (get_local $$and15)
+ (get_local $$sub)
+ )
+ )
+ (set_local $$lnot
+ (i32.eq
+ (get_local $$and16)
+ (i32.const 0)
+ )
+ )
+ (if
+ (i32.eqz
+ (get_local $$lnot)
+ )
+ (br $while-out2)
+ )
+ (set_local $$incdec$ptr21
+ (i32.add
+ (get_local $$w$034)
+ (i32.const 4)
+ )
+ )
+ (set_local $$sub22
+ (i32.add
+ (get_local $$n$addr$133)
+ (i32.const -4)
+ )
+ )
+ (set_local $$cmp11
+ (i32.gt_u
+ (get_local $$sub22)
+ (i32.const 3)
+ )
+ )
+ (if
+ (get_local $$cmp11)
+ (block
+ (set_local $$n$addr$133
+ (get_local $$sub22)
+ )
+ (set_local $$w$034
+ (get_local $$incdec$ptr21)
+ )
+ )
+ (block
+ (set_local $$n$addr$1$lcssa
+ (get_local $$sub22)
+ )
+ (set_local $$w$0$lcssa
+ (get_local $$incdec$ptr21)
+ )
+ (set_local $label
+ (i32.const 11)
+ )
+ (br $label$break$L11)
+ )
+ )
+ (br $while-in3)
+ )
+ )
+ (set_local $$n$addr$227
+ (get_local $$n$addr$133)
+ )
+ (set_local $$s$128
+ (get_local $$w$034)
+ )
+ )
+ (block
+ (set_local $$n$addr$1$lcssa
+ (get_local $$n$addr$0$lcssa52)
+ )
+ (set_local $$w$0$lcssa
+ (get_local $$s$0$lcssa53)
+ )
+ (set_local $label
+ (i32.const 11)
+ )
+ )
+ )
+ )
+ (if
+ (i32.eq
+ (get_local $label)
+ (i32.const 11)
+ )
+ (block
+ (set_local $$tobool2526
+ (i32.eq
+ (get_local $$n$addr$1$lcssa)
+ (i32.const 0)
+ )
+ )
+ (if
+ (get_local $$tobool2526)
+ (block
+ (set_local $$n$addr$3
+ (i32.const 0)
+ )
+ (set_local $$s$2
+ (get_local $$w$0$lcssa)
+ )
+ (br $label$break$L8)
+ )
+ (block
+ (set_local $$n$addr$227
+ (get_local $$n$addr$1$lcssa)
+ )
+ (set_local $$s$128
+ (get_local $$w$0$lcssa)
+ )
+ )
+ )
+ )
+ )
+ (loop $while-in5
+ (block $while-out4
+ (set_local $$7
+ (i32.load8_s
+ (get_local $$s$128)
+ )
+ )
+ (set_local $$cmp28
+ (i32.eq
+ (i32.shr_s
+ (i32.shl
+ (get_local $$7)
+ (i32.const 24)
+ )
+ (i32.const 24)
+ )
+ (i32.shr_s
+ (i32.shl
+ (get_local $$5)
+ (i32.const 24)
+ )
+ (i32.const 24)
+ )
+ )
+ )
+ (if
+ (get_local $$cmp28)
+ (block
+ (set_local $$n$addr$3
+ (get_local $$n$addr$227)
+ )
+ (set_local $$s$2
+ (get_local $$s$128)
+ )
+ (br $label$break$L8)
+ )
+ )
+ (set_local $$incdec$ptr33
+ (i32.add
+ (get_local $$s$128)
+ (i32.const 1)
+ )
+ )
+ (set_local $$dec34
+ (i32.add
+ (get_local $$n$addr$227)
+ (i32.const -1)
+ )
+ )
+ (set_local $$tobool25
+ (i32.eq
+ (get_local $$dec34)
+ (i32.const 0)
+ )
+ )
+ (if
+ (get_local $$tobool25)
+ (block
+ (set_local $$n$addr$3
+ (i32.const 0)
+ )
+ (set_local $$s$2
+ (get_local $$incdec$ptr33)
+ )
+ (br $while-out4)
+ )
+ (block
+ (set_local $$n$addr$227
+ (get_local $$dec34)
+ )
+ (set_local $$s$128
+ (get_local $$incdec$ptr33)
+ )
+ )
+ )
+ (br $while-in5)
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ (set_local $$tobool36
+ (i32.ne
+ (get_local $$n$addr$3)
+ (i32.const 0)
+ )
+ )
+ (set_local $$cond
+ (if i32
+ (get_local $$tobool36)
+ (get_local $$s$2)
+ (i32.const 0)
+ )
+ )
+ (return
+ (get_local $$cond)
+ )
+ )
(func $keepAlive
(call $loads)
(call $stores)
@@ -995,6 +1575,18 @@
(i32.const 0)
(i32.const 0)
)
+ (drop
+ (call $_memchr
+ (i32.const 0)
+ (i32.const 0)
+ (i32.const 0)
+ )
+ )
+ (drop
+ (call $switch64TOOMUCH
+ (i64.const 0)
+ )
+ )
)
(func $__emscripten_dceable_type_decls
(drop