diff --git a/src/passes/MergeBlocks.cpp b/src/passes/MergeBlocks.cpp index d26817e1851..5794e1379ca 100644 --- a/src/passes/MergeBlocks.cpp +++ b/src/passes/MergeBlocks.cpp @@ -17,9 +17,53 @@ // // Merges blocks to their parents. // +// We also restructure blocks in order to enable such merging. For +// example, +// +// (i32.store +// (block +// (call $foo) +// (i32.load (i32.const 100)) +// ) +// (i32.const 0) +// ) +// +// can be transformed into +// +// (block +// (call $foo) +// (i32.store +// (block +// (i32.load (i32.const 100)) +// ) +// (i32.const 0) +// ) +// ) +// +// after which the internal block can go away, and +// the new external block might be mergeable. This is always +// worth it if the internal block ends up with 1 item. +// For the second operand, +// +// (i32.store +// (i32.const 100) +// (block +// (call $foo) +// (i32.load (i32.const 200)) +// ) +// ) +// +// The order of operations requires that the first execute +// before. We can do the same operation, but only if the +// first has no side effects, or the code we are moving out +// has no side effects. +// If we can do this to both operands, we can generate a +// single outside block. +// #include #include +#include namespace wasm { @@ -50,6 +94,86 @@ struct MergeBlocks : public WalkerPassdynCast()) { + if (block->list.size() >= 2) { + child = block->list.back(); + 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 + replaceCurrent(block); + return block; + } else { + // append to an existing outer block + assert(outer->list.back() == curr); + outer->list.pop_back(); + for (Index i = 0; i < block->list.size() - 1; i++) { + outer->list.push_back(block->list[i]); + } + outer->list.push_back(curr); + } + } + } + return outer; + } + + void visitUnary(Unary* curr) { + optimize(curr, curr->value); + } + void visitSetLocal(SetLocal* curr) { + optimize(curr, curr->value); + } + void visitLoad(Load* curr) { + optimize(curr, curr->ptr); + } + void visitReturn(Return* curr) { + optimize(curr, curr->value); + } + + void visitBinary(Binary* curr) { + optimize(curr, curr->right, optimize(curr, curr->left), &curr->left); + } + void visitStore(Store* curr) { + optimize(curr, curr->value, optimize(curr, curr->ptr), &curr->ptr); + } + + void visitSelect(Select* curr) { + optimize(curr, curr->condition, optimize(curr, curr->ifFalse, optimize(curr, curr->ifTrue), &curr->ifTrue), &curr->ifTrue, &curr->ifFalse); + } + + void visitBreak(Break* curr) { + optimize(curr, curr->condition, optimize(curr, curr->value), &curr->value); + } + void visitSwitch(Switch* curr) { + optimize(curr, curr->condition, optimize(curr, curr->value), &curr->value); + } + + template + void handleCall(T* curr, Block* outer = nullptr) { + for (Index i = 0; i < curr->operands.size(); i++) { + outer = optimize(curr, curr->operands[i], outer); + if (EffectAnalyzer(curr->operands[i]).hasSideEffects()) return; + } + } + + void visitCall(Call* curr) { + handleCall(curr); + } + + void visitCallImport(CallImport* curr) { + handleCall(curr); + } + + void visitCallIndirect(CallIndirect* curr) { + auto* outer = optimize(curr, curr->target); + if (EffectAnalyzer(curr->target).hasSideEffects()) return; + handleCall(curr, outer); + } }; static RegisterPass registerPass("merge-blocks", "merges blocks to their parents"); diff --git a/src/tools/binaryen-shell.cpp b/src/tools/binaryen-shell.cpp index 8f5fbe4e5b4..96f506934bf 100644 --- a/src/tools/binaryen-shell.cpp +++ b/src/tools/binaryen-shell.cpp @@ -198,50 +198,51 @@ int main(int argc, const char* argv[]) { auto input(read_file>(options.extra["infile"], Flags::Text, options.debug ? Flags::Debug : Flags::Release)); - if (options.debug) std::cerr << "parsing text to s-expressions...\n"; - SExpressionParser parser(input.data()); - Element& root = *parser.root; - - // A .wast may have multiple modules, with some asserts after them bool checked = false; - size_t i = 0; - while (i < root.size()) { - Element& curr = *root[i]; - IString id = curr[0]->str(); - if (id == MODULE) { - if (options.debug) std::cerr << "parsing s-expressions to wasm...\n"; - Module wasm; - std::unique_ptr builder; - try { + + try { + if (options.debug) std::cerr << "parsing text to s-expressions...\n"; + SExpressionParser parser(input.data()); + Element& root = *parser.root; + + // A .wast may have multiple modules, with some asserts after them + size_t i = 0; + while (i < root.size()) { + Element& curr = *root[i]; + IString id = curr[0]->str(); + if (id == MODULE) { + if (options.debug) std::cerr << "parsing s-expressions to wasm...\n"; + Module wasm; + std::unique_ptr builder; builder = make_unique(wasm, *root[i]); - } catch (ParseException& p) { - p.dump(std::cerr); - abort(); - } - i++; - assert(WasmValidator().validate(wasm)); - - MixedArena moreModuleAllocations; - - if (passes.size() > 0) { - if (options.debug) std::cerr << "running passes...\n"; - PassRunner passRunner(&wasm); - if (options.debug) passRunner.setDebug(true); - for (auto& passName : passes) { - if (passName == "O") { - passRunner.addDefaultOptimizationPasses(); - } else { - passRunner.add(passName); + i++; + assert(WasmValidator().validate(wasm)); + + MixedArena moreModuleAllocations; + + if (passes.size() > 0) { + if (options.debug) std::cerr << "running passes...\n"; + PassRunner passRunner(&wasm); + if (options.debug) passRunner.setDebug(true); + for (auto& passName : passes) { + if (passName == "O") { + passRunner.addDefaultOptimizationPasses(); + } else { + passRunner.add(passName); + } } + passRunner.run(); + assert(WasmValidator().validate(wasm)); } - passRunner.run(); - assert(WasmValidator().validate(wasm)); - } - run_asserts(&i, &checked, &wasm, &root, &builder, entry); - } else { - run_asserts(&i, &checked, nullptr, &root, nullptr, entry); + run_asserts(&i, &checked, &wasm, &root, &builder, entry); + } else { + run_asserts(&i, &checked, nullptr, &root, nullptr, entry); + } } + } catch (ParseException& p) { + p.dump(std::cerr); + abort(); } if (checked) { diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index dae32186693..bb6fe3fd459 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -173,7 +173,7 @@ class SExpressionParser { curr->list().push_back(parseString()); } } - assert(stack.size() == 0); + if (stack.size() != 0) throw ParseException("stack is not empty", curr->line, curr->col); return curr; } diff --git a/test/emcc_hello_world.fromasm b/test/emcc_hello_world.fromasm index 6f6da8ed3e7..d5e961a9221 100644 --- a/test/emcc_hello_world.fromasm +++ b/test/emcc_hello_world.fromasm @@ -19091,16 +19091,14 @@ ) ) ) + (i32.store + (i32.const 168) + (i32.const 0) + ) (return - (block - (i32.store - (i32.const 168) - (i32.const 0) - ) - (i32.div_u - (get_local $7) - (get_local $5) - ) + (i32.div_u + (get_local $7) + (get_local $5) ) ) ) @@ -19109,12 +19107,12 @@ (i32.eqz (get_local $2) ) - (return - (block - (i32.store - (i32.const 168) - (i32.const 0) - ) + (block + (i32.store + (i32.const 168) + (i32.const 0) + ) + (return (i32.const 0) ) ) @@ -19133,14 +19131,12 @@ (i32.const 0) ) ) + (i32.store + (i32.const 168) + (i32.const 0) + ) (return - (block - (i32.store - (i32.const 168) - (i32.const 0) - ) - (i32.const 0) - ) + (i32.const 0) ) ) ) @@ -19181,16 +19177,14 @@ ) ) ) + (i32.store + (i32.const 168) + (i32.const 0) + ) (return - (block - (i32.store - (i32.const 168) - (i32.const 0) - ) - (i32.div_u - (get_local $6) - (get_local $5) - ) + (i32.div_u + (get_local $6) + (get_local $5) ) ) ) @@ -19220,16 +19214,14 @@ ) ) ) + (i32.store + (i32.const 168) + (i32.const 0) + ) (return - (block - (i32.store - (i32.const 168) - (i32.const 0) - ) - (i32.div_u - (get_local $6) - (get_local $8) - ) + (i32.div_u + (get_local $6) + (get_local $8) ) ) ) @@ -19279,17 +19271,15 @@ ) ) ) + (i32.store + (i32.const 168) + (i32.const 0) + ) (return - (block - (i32.store - (i32.const 168) - (i32.const 0) - ) - (i32.shr_u - (get_local $6) - (i32.ctz - (get_local $8) - ) + (i32.shr_u + (get_local $6) + (i32.ctz + (get_local $8) ) ) ) @@ -19358,12 +19348,12 @@ (get_local $4) (i32.const 0) ) - (return - (block - (i32.store - (i32.const 168) - (i32.const 0) - ) + (block + (i32.store + (i32.const 168) + (i32.const 0) + ) + (return (i32.const 0) ) ) @@ -19388,14 +19378,12 @@ ) ) ) + (i32.store + (i32.const 168) + (i32.const 0) + ) (return - (block - (i32.store - (i32.const 168) - (i32.const 0) - ) - (i32.const 0) - ) + (i32.const 0) ) ) (block @@ -19481,12 +19469,12 @@ (get_local $4) (i32.const 0) ) - (return - (block - (i32.store - (i32.const 168) - (i32.const 0) - ) + (block + (i32.store + (i32.const 168) + (i32.const 0) + ) + (return (i32.const 0) ) ) @@ -19511,14 +19499,12 @@ ) ) ) + (i32.store + (i32.const 168) + (i32.const 0) + ) (return - (block - (i32.store - (i32.const 168) - (i32.const 0) - ) - (i32.const 0) - ) + (i32.const 0) ) ) ) @@ -19684,18 +19670,18 @@ (get_local $5) (i32.const 1) ) - (return - (block - (i32.store - (i32.const 168) - (i32.or - (get_local $9) - (i32.and - (get_local $1) - (i32.const 0) - ) + (block + (i32.store + (i32.const 168) + (i32.or + (get_local $9) + (i32.and + (get_local $1) + (i32.const 0) ) ) + ) + (return (i32.or (i32.const 0) (i32.and @@ -19705,22 +19691,22 @@ ) ) ) - (return - (block - (i32.store - (i32.const 168) - (i32.or - (i32.const 0) - (i32.shr_u - (get_local $6) - (set_local $0 - (i32.ctz - (get_local $5) - ) + (block + (i32.store + (i32.const 168) + (i32.or + (i32.const 0) + (i32.shr_u + (get_local $6) + (set_local $0 + (i32.ctz + (get_local $5) ) ) ) ) + ) + (return (i32.or (i32.shl (get_local $6) diff --git a/test/emcc_hello_world.fromasm.imprecise b/test/emcc_hello_world.fromasm.imprecise index fac02cca9bf..0b7f2497b14 100644 --- a/test/emcc_hello_world.fromasm.imprecise +++ b/test/emcc_hello_world.fromasm.imprecise @@ -19089,16 +19089,14 @@ ) ) ) + (i32.store + (i32.const 168) + (i32.const 0) + ) (return - (block - (i32.store - (i32.const 168) - (i32.const 0) - ) - (i32.div_u - (get_local $7) - (get_local $5) - ) + (i32.div_u + (get_local $7) + (get_local $5) ) ) ) @@ -19107,12 +19105,12 @@ (i32.eqz (get_local $2) ) - (return - (block - (i32.store - (i32.const 168) - (i32.const 0) - ) + (block + (i32.store + (i32.const 168) + (i32.const 0) + ) + (return (i32.const 0) ) ) @@ -19131,14 +19129,12 @@ (i32.const 0) ) ) + (i32.store + (i32.const 168) + (i32.const 0) + ) (return - (block - (i32.store - (i32.const 168) - (i32.const 0) - ) - (i32.const 0) - ) + (i32.const 0) ) ) ) @@ -19179,16 +19175,14 @@ ) ) ) + (i32.store + (i32.const 168) + (i32.const 0) + ) (return - (block - (i32.store - (i32.const 168) - (i32.const 0) - ) - (i32.div_u - (get_local $6) - (get_local $5) - ) + (i32.div_u + (get_local $6) + (get_local $5) ) ) ) @@ -19218,16 +19212,14 @@ ) ) ) + (i32.store + (i32.const 168) + (i32.const 0) + ) (return - (block - (i32.store - (i32.const 168) - (i32.const 0) - ) - (i32.div_u - (get_local $6) - (get_local $8) - ) + (i32.div_u + (get_local $6) + (get_local $8) ) ) ) @@ -19277,17 +19269,15 @@ ) ) ) + (i32.store + (i32.const 168) + (i32.const 0) + ) (return - (block - (i32.store - (i32.const 168) - (i32.const 0) - ) - (i32.shr_u - (get_local $6) - (i32.ctz - (get_local $8) - ) + (i32.shr_u + (get_local $6) + (i32.ctz + (get_local $8) ) ) ) @@ -19356,12 +19346,12 @@ (get_local $4) (i32.const 0) ) - (return - (block - (i32.store - (i32.const 168) - (i32.const 0) - ) + (block + (i32.store + (i32.const 168) + (i32.const 0) + ) + (return (i32.const 0) ) ) @@ -19386,14 +19376,12 @@ ) ) ) + (i32.store + (i32.const 168) + (i32.const 0) + ) (return - (block - (i32.store - (i32.const 168) - (i32.const 0) - ) - (i32.const 0) - ) + (i32.const 0) ) ) (block @@ -19479,12 +19467,12 @@ (get_local $4) (i32.const 0) ) - (return - (block - (i32.store - (i32.const 168) - (i32.const 0) - ) + (block + (i32.store + (i32.const 168) + (i32.const 0) + ) + (return (i32.const 0) ) ) @@ -19509,14 +19497,12 @@ ) ) ) + (i32.store + (i32.const 168) + (i32.const 0) + ) (return - (block - (i32.store - (i32.const 168) - (i32.const 0) - ) - (i32.const 0) - ) + (i32.const 0) ) ) ) @@ -19682,18 +19668,18 @@ (get_local $5) (i32.const 1) ) - (return - (block - (i32.store - (i32.const 168) - (i32.or - (get_local $9) - (i32.and - (get_local $1) - (i32.const 0) - ) + (block + (i32.store + (i32.const 168) + (i32.or + (get_local $9) + (i32.and + (get_local $1) + (i32.const 0) ) ) + ) + (return (i32.or (i32.const 0) (i32.and @@ -19703,22 +19689,22 @@ ) ) ) - (return - (block - (i32.store - (i32.const 168) - (i32.or - (i32.const 0) - (i32.shr_u - (get_local $6) - (set_local $0 - (i32.ctz - (get_local $5) - ) + (block + (i32.store + (i32.const 168) + (i32.or + (i32.const 0) + (i32.shr_u + (get_local $6) + (set_local $0 + (i32.ctz + (get_local $5) ) ) ) ) + ) + (return (i32.or (i32.shl (get_local $6) diff --git a/test/min.fromasm b/test/min.fromasm index 45b2f8c1927..51750dd531a 100644 --- a/test/min.fromasm +++ b/test/min.fromasm @@ -10,15 +10,13 @@ ) ) (func $neg (param $0 i32) (param $1 i32) (result f32) + (i32.store + (get_local $0) + (get_local $1) + ) (f32.neg - (block - (i32.store - (get_local $0) - (get_local $1) - ) - (f32.load - (get_local $0) - ) + (f32.load + (get_local $0) ) ) ) diff --git a/test/min.fromasm.imprecise b/test/min.fromasm.imprecise index 45b2f8c1927..51750dd531a 100644 --- a/test/min.fromasm.imprecise +++ b/test/min.fromasm.imprecise @@ -10,15 +10,13 @@ ) ) (func $neg (param $0 i32) (param $1 i32) (result f32) + (i32.store + (get_local $0) + (get_local $1) + ) (f32.neg - (block - (i32.store - (get_local $0) - (get_local $1) - ) - (f32.load - (get_local $0) - ) + (f32.load + (get_local $0) ) ) ) diff --git a/test/passes/merge-blocks.txt b/test/passes/merge-blocks.txt index 2b4278f7c83..f5d772c7495 100644 --- a/test/passes/merge-blocks.txt +++ b/test/passes/merge-blocks.txt @@ -1,5 +1,18 @@ (module (memory 256 256) + (type $i (func (param i32))) + (type $ii (func (param i32 i32))) + (type $iii (func (param i32 i32 i32))) + (table $call-i) + (func $call-i (param $0 i32) + (nop) + ) + (func $call-ii (param $0 i32) (param $1 i32) + (nop) + ) + (func $call-iii (param $0 i32) (param $1 i32) (param $2 i32) + (nop) + ) (func $b0-yes (param $i1 i32) (block $topmost (block $block0 @@ -82,4 +95,443 @@ (i32.const 20) ) ) + (func $unary + (local $x i32) + (i32.eqz + (block $block0 + (i32.const 10) + ) + ) + (block $block1 + (i32.const 10) + (i32.eqz + (i32.const 20) + ) + ) + (block $block2 + (i32.const 10) + (i32.const 20) + (i32.eqz + (i32.const 30) + ) + ) + (block $block3 + (i32.const 10) + (set_local $x + (i32.const 20) + ) + ) + (block $block4 + (i32.const 10) + (i32.load + (i32.const 20) + ) + ) + (block $block5 + (i32.const 10) + (return + (unreachable) + ) + ) + ) + (func $binary + (i32.add + (block $block0 + (i32.const 10) + ) + (i32.const 20) + ) + (block $block1 + (i32.const 10) + (i32.add + (i32.const 20) + (i32.const 30) + ) + ) + (block $block2 + (i32.const 10) + (i32.const 20) + (i32.add + (i32.const 30) + (i32.const 40) + ) + ) + (i32.add + (i32.const 10) + (block $block3 + (i32.const 20) + ) + ) + (block $block4 + (i32.const 20) + (i32.add + (i32.const 10) + (i32.const 30) + ) + ) + (block $block5 + (i32.const 20) + (i32.const 30) + (i32.add + (i32.const 10) + (i32.const 40) + ) + ) + (i32.add + (block $block6 + (i32.const 10) + ) + (block $block7 + (i32.const 20) + ) + ) + (block $block8 + (i32.const 10) + (i32.const 30) + (i32.add + (i32.const 20) + (i32.const 40) + ) + ) + (block $block10 + (i32.const 10) + (i32.const 20) + (i32.const 40) + (i32.const 50) + (i32.add + (i32.const 30) + (i32.const 60) + ) + ) + (block $block12 + (i32.const 20) + (i32.store + (i32.const 10) + (i32.const 30) + ) + ) + (block $block13 + (i32.const 10) + (i32.store + (i32.const 20) + (i32.const 30) + ) + ) + (i32.add + (unreachable) + (block $block14 + (i32.const 10) + (i32.const 20) + ) + ) + (block $block15 + (unreachable) + (i32.const 20) + (i32.add + (i32.const 10) + (i32.const 30) + ) + ) + ) + (func $trinary + (block $block0 + (i32.const 10) + (i32.const 30) + (i32.const 50) + (select + (i32.const 20) + (i32.const 40) + (i32.const 60) + ) + ) + (block $block4 + (i32.const 20) + (i32.const 40) + (select + (block $block3 + (i32.const 10) + ) + (i32.const 30) + (i32.const 50) + ) + ) + (block $block6 + (i32.const 10) + (i32.const 40) + (select + (i32.const 20) + (block $block7 + (i32.const 30) + ) + (i32.const 50) + ) + ) + (block $block9 + (i32.const 10) + (i32.const 30) + (select + (i32.const 20) + (i32.const 40) + (block $block11 + (i32.const 50) + ) + ) + ) + (block $block14 + (i32.const 30) + (select + (block $block12 + (i32.const 10) + ) + (block $block13 + (i32.const 20) + ) + (i32.const 40) + ) + ) + (block $block16 + (i32.const 20) + (select + (block $block15 + (i32.const 10) + ) + (i32.const 30) + (block $block17 + (i32.const 40) + ) + ) + ) + (block $block18 + (i32.const 10) + (select + (i32.const 20) + (block $block19 + (i32.const 30) + ) + (block $block20 + (i32.const 40) + ) + ) + ) + (block $block21 + (unreachable) + (i32.const 30) + (i32.const 50) + (select + (i32.const 20) + (i32.const 40) + (i32.const 60) + ) + ) + (block $block24 + (i32.const 10) + (select + (unreachable) + (block $block25 + (i32.const 30) + (i32.const 40) + ) + (block $block26 + (i32.const 50) + (i32.const 60) + ) + ) + ) + (block $block27 + (i32.const 10) + (unreachable) + (i32.const 50) + (select + (i32.const 20) + (i32.const 40) + (i32.const 60) + ) + ) + (block $block30 + (i32.const 10) + (i32.const 30) + (select + (i32.const 20) + (unreachable) + (block $block32 + (i32.const 50) + (i32.const 60) + ) + ) + ) + (block $block33 + (i32.const 10) + (i32.const 30) + (unreachable) + (select + (i32.const 20) + (i32.const 40) + (i32.const 60) + ) + ) + (block $block36 + (i32.const 10) + (i32.const 30) + (i32.const 50) + (select + (i32.const 20) + (i32.const 40) + (unreachable) + ) + ) + ) + (func $breaks + (block $out + (block $block0 + (i32.const 10) + (br $out + (i32.const 20) + ) + ) + (block $block1 + (i32.const 10) + (br_if $out + (i32.const 20) + ) + ) + (block $block2 + (i32.const 10) + (i32.const 30) + (br_if $out + (i32.const 20) + (i32.const 40) + ) + ) + (block $block4 + (i32.const 10) + (br_table $out $out + (i32.const 20) + ) + ) + (block $block5 + (i32.const 10) + (i32.const 30) + (br_table $out $out + (i32.const 20) + (i32.const 40) + ) + ) + ) + ) + (func $calls + (call $call-i + (block $block0 + (i32.const 10) + ) + ) + (block $block1 + (i32.const 10) + (call $call-i + (i32.const 20) + ) + ) + (block $block2 + (i32.const 10) + (i32.const 20) + (call $call-i + (i32.const 30) + ) + ) + (block $block3 + (i32.const 10) + (i32.const 30) + (call $call-ii + (i32.const 20) + (i32.const 40) + ) + ) + (block $block5 + (unreachable) + (i32.const 20) + (call $call-ii + (i32.const 10) + (i32.const 30) + ) + ) + (block $block7 + (i32.const 10) + (call $call-ii + (unreachable) + (block $block8 + (i32.const 20) + (i32.const 30) + ) + ) + ) + (block $block9 + (i32.const 10) + (unreachable) + (call $call-ii + (i32.const 20) + (i32.const 30) + ) + ) + (block $block11 + (i32.const 10) + (i32.const 30) + (call $call-ii + (i32.const 20) + (unreachable) + ) + ) + (block $block13 + (i32.const 10) + (i32.const 30) + (i32.const 50) + (call $call-iii + (i32.const 20) + (i32.const 40) + (i32.const 60) + ) + ) + (block $block16 + (i32.const 10) + (i32.const 40) + (call $call-iii + (i32.const 20) + (i32.const 30) + (i32.const 50) + ) + ) + (block $block18 + (i32.const 10) + (i32.const 30) + (i32.const 50) + (call_indirect $ii + (i32.const 20) + (i32.const 40) + (i32.const 60) + ) + ) + (call_indirect $ii + (unreachable) + (block $block21 + (i32.const 30) + (i32.const 40) + ) + (block $block22 + (i32.const 50) + (i32.const 60) + ) + ) + ) + (func $block-type-change + (local $0 f64) + (local $1 f64) + (if + (block $block0 + (nop) + (f64.gt + (get_local $0) + (get_local $1) + ) + ) + (nop) + ) + ) ) diff --git a/test/passes/merge-blocks.wast b/test/passes/merge-blocks.wast index 09eefefdb4e..a4a9873e900 100644 --- a/test/passes/merge-blocks.wast +++ b/test/passes/merge-blocks.wast @@ -1,5 +1,15 @@ (module (memory 256 256) + (type $i (func (param i32))) + (type $ii (func (param i32) (param i32))) + (type $iii (func (param i32) (param i32) (param i32))) + (table $call-i) + (func $call-i (param i32) + ) + (func $call-ii (param i32) (param i32) + ) + (func $call-iii (param i32) (param i32) (param i32) + ) (func $b0-yes (param $i1 i32) (block $topmost (block @@ -82,5 +92,501 @@ (i32.const 20) ) ) + (func $unary + (local $x i32) + (i32.eqz + (block + (i32.const 10) + ) + ) + (i32.eqz + (block + (i32.const 10) + (i32.const 20) + ) + ) + (i32.eqz + (block + (i32.const 10) + (i32.const 20) + (i32.const 30) + ) + ) + (set_local $x + (block + (i32.const 10) + (i32.const 20) + ) + ) + (i32.load + (block + (i32.const 10) + (i32.const 20) + ) + ) + (return + (block + (i32.const 10) + (unreachable) + ) + ) + ) + (func $binary + (i32.add + (block + (i32.const 10) + ) + (i32.const 20) + ) + (i32.add + (block + (i32.const 10) + (i32.const 20) + ) + (i32.const 30) + ) + (i32.add + (block + (i32.const 10) + (i32.const 20) + (i32.const 30) + ) + (i32.const 40) + ) + (i32.add + (i32.const 10) + (block + (i32.const 20) + ) + ) + (i32.add + (i32.const 10) + (block + (i32.const 20) + (i32.const 30) + ) + ) + (i32.add + (i32.const 10) + (block + (i32.const 20) + (i32.const 30) + (i32.const 40) + ) + ) + (i32.add + (block + (i32.const 10) + ) + (block + (i32.const 20) + ) + ) + (i32.add + (block + (i32.const 10) + (i32.const 20) + ) + (block + (i32.const 30) + (i32.const 40) + ) + ) + (i32.add + (block + (i32.const 10) + (i32.const 20) + (i32.const 30) + ) + (block + (i32.const 40) + (i32.const 50) + (i32.const 60) + ) + ) + (i32.store + (i32.const 10) + (block + (i32.const 20) + (i32.const 30) + ) + ) + (i32.store + (block + (i32.const 10) + (i32.const 20) + ) + (i32.const 30) + ) + (i32.add + (unreachable) ;; do not move across this TODO: move non-side-effecting + (block + (i32.const 10) + (i32.const 20) + ) + ) + (i32.add + (block + (unreachable) ;; moves out, so does not block the rest + (i32.const 10) + ) + (block + (i32.const 20) + (i32.const 30) + ) + ) + ) + (func $trinary + (select + (block + (i32.const 10) + (i32.const 20) + ) + (block + (i32.const 30) + (i32.const 40) + ) + (block + (i32.const 50) + (i32.const 60) + ) + ) + (select + (block + (i32.const 10) + ) + (block + (i32.const 20) + (i32.const 30) + ) + (block + (i32.const 40) + (i32.const 50) + ) + ) + (select + (block + (i32.const 10) + (i32.const 20) + ) + (block + (i32.const 30) + ) + (block + (i32.const 40) + (i32.const 50) + ) + ) + (select + (block + (i32.const 10) + (i32.const 20) + ) + (block + (i32.const 30) + (i32.const 40) + ) + (block + (i32.const 50) + ) + ) + (select + (block + (i32.const 10) + ) + (block + (i32.const 20) + ) + (block + (i32.const 30) + (i32.const 40) + ) + ) + (select + (block + (i32.const 10) + ) + (block + (i32.const 20) + (i32.const 30) + ) + (block + (i32.const 40) + ) + ) + (select + (block + (i32.const 10) + (i32.const 20) + ) + (block + (i32.const 30) + ) + (block + (i32.const 40) + ) + ) + ;; now for bad stuff + (select + (block + (unreachable) + (i32.const 20) + ) + (block + (i32.const 30) + (i32.const 40) + ) + (block + (i32.const 50) + (i32.const 60) + ) + ) + (select + (block + (i32.const 10) + (unreachable) + ) + (block + (i32.const 30) + (i32.const 40) + ) + (block + (i32.const 50) + (i32.const 60) + ) + ) + (select + (block + (i32.const 10) + (i32.const 20) + ) + (block + (unreachable) + (i32.const 40) + ) + (block + (i32.const 50) + (i32.const 60) + ) + ) + (select + (block + (i32.const 10) + (i32.const 20) + ) + (block + (i32.const 30) + (unreachable) + ) + (block + (i32.const 50) + (i32.const 60) + ) + ) + (select + (block + (i32.const 10) + (i32.const 20) + ) + (block + (i32.const 30) + (i32.const 40) + ) + (block + (unreachable) + (i32.const 60) + ) + ) + (select + (block + (i32.const 10) + (i32.const 20) + ) + (block + (i32.const 30) + (i32.const 40) + ) + (block + (i32.const 50) + (unreachable) + ) + ) + ) + (func $breaks + (block $out + (br $out + (block + (i32.const 10) + (i32.const 20) + ) + ) + (br_if $out + (block + (i32.const 10) + (i32.const 20) + ) + ) + (br_if $out + (block + (i32.const 10) + (i32.const 20) + ) + (block + (i32.const 30) + (i32.const 40) + ) + ) + (br_table $out $out + (block + (i32.const 10) + (i32.const 20) + ) + ) + (br_table $out $out + (block + (i32.const 10) + (i32.const 20) + ) + (block + (i32.const 30) + (i32.const 40) + ) + ) + ) + ) + (func $calls + (call $call-i + (block + (i32.const 10) + ) + ) + (call $call-i + (block + (i32.const 10) + (i32.const 20) + ) + ) + (call $call-i + (block + (i32.const 10) + (i32.const 20) + (i32.const 30) + ) + ) + (call $call-ii + (block + (i32.const 10) + (i32.const 20) + ) + (block + (i32.const 30) + (i32.const 40) + ) + ) + (call $call-ii + (block + (unreachable) + (i32.const 10) + ) + (block + (i32.const 20) + (i32.const 30) + ) + ) + (call $call-ii + (block + (i32.const 10) + (unreachable) + ) + (block + (i32.const 20) + (i32.const 30) + ) + ) + (call $call-ii + (block + (i32.const 10) + (i32.const 20) + ) + (block + (unreachable) + (i32.const 30) + ) + ) + (call $call-ii + (block + (i32.const 10) + (i32.const 20) + ) + (block + (i32.const 30) + (unreachable) + ) + ) + (call $call-iii + (block + (i32.const 10) + (i32.const 20) + ) + (block + (i32.const 30) + (i32.const 40) + ) + (block + (i32.const 50) + (i32.const 60) + ) + ) + (call $call-iii + (block + (i32.const 10) + (i32.const 20) + ) + (i32.const 30) + (block + (i32.const 40) + (i32.const 50) + ) + ) + (call_indirect $ii + (block + (i32.const 10) + (i32.const 20) + ) + (block + (i32.const 30) + (i32.const 40) + ) + (block + (i32.const 50) + (i32.const 60) + ) + ) + (call_indirect $ii + (unreachable) + (block + (i32.const 30) + (i32.const 40) + ) + (block + (i32.const 50) + (i32.const 60) + ) + ) + ) + (func $block-type-change + (local $0 f64) + (local $1 f64) + (if + (f64.gt + (get_local $0) + (block + (nop) + (get_local $1) + ) + ) + (nop) + ) + ) ) diff --git a/test/unit.asm.js b/test/unit.asm.js index 77fbe408bd1..8fb28b99074 100644 --- a/test/unit.asm.js +++ b/test/unit.asm.js @@ -214,11 +214,17 @@ function asm(global, env, buffer) { (HEAPF32[tempDoublePtr >> 2] = f, HEAP32[tempDoublePtr >> 2] | 0); // f32->i32 (HEAPF32[tempDoublePtr >> 2] = d, HEAP32[tempDoublePtr >> 2] | 0); // f64 with implict f32 conversion, ->i32 } + function recursiveBlockMerging(x) { + x = x | 0; + lb((1, x) + (2, 3) + (((4, 5), 6), 7) + (8, (9, (10, (11, 12))))) | 0; + x = (lb(1) | 0, x) + (lb(2) | 0, lb(3) | 0) + (((lb(4) | 0, lb(5) | 0), lb(6) | 0), lb(7) | 0) + (lb(8) | 0, (lb(9) | 0, (lb(10) | 0, (lb(11) | 0, lb(12) | 0)))) | 0; + return x | 0; + } function lb(a) { a = a | 0; HEAP32[a >> 2] = n + 136 + 8; - return; + return 0; } function z() { diff --git a/test/unit.fromasm b/test/unit.fromasm index 23f6c48bada..f828c415a76 100644 --- a/test/unit.fromasm +++ b/test/unit.fromasm @@ -363,7 +363,68 @@ (func $bitcasts (param $0 i32) (param $1 f32) (nop) ) - (func $lb (param $0 i32) + (func $recursiveBlockMerging (param $0 i32) (result i32) + (call $lb + (i32.add + (i32.add + (i32.add + (get_local $0) + (i32.const 3) + ) + (i32.const 7) + ) + (i32.const 12) + ) + ) + (call $lb + (i32.const 1) + ) + (call $lb + (i32.const 2) + ) + (i32.add + (i32.add + (i32.add + (get_local $0) + (call $lb + (i32.const 3) + ) + ) + (block + (call $lb + (i32.const 4) + ) + (call $lb + (i32.const 5) + ) + (call $lb + (i32.const 6) + ) + (call $lb + (i32.const 7) + ) + ) + ) + (block + (call $lb + (i32.const 8) + ) + (call $lb + (i32.const 9) + ) + (call $lb + (i32.const 10) + ) + (call $lb + (i32.const 11) + ) + (call $lb + (i32.const 12) + ) + ) + ) + ) + (func $lb (param $0 i32) (result i32) (i32.store (get_local $0) (i32.add @@ -376,6 +437,7 @@ (i32.const 8) ) ) + (i32.const 0) ) (func $z (nop) diff --git a/test/unit.fromasm.imprecise b/test/unit.fromasm.imprecise index 347ea4e8845..1bb1ae48508 100644 --- a/test/unit.fromasm.imprecise +++ b/test/unit.fromasm.imprecise @@ -355,7 +355,68 @@ (func $bitcasts (param $0 i32) (param $1 f32) (nop) ) - (func $lb (param $0 i32) + (func $recursiveBlockMerging (param $0 i32) (result i32) + (call $lb + (i32.add + (i32.add + (i32.add + (get_local $0) + (i32.const 3) + ) + (i32.const 7) + ) + (i32.const 12) + ) + ) + (call $lb + (i32.const 1) + ) + (call $lb + (i32.const 2) + ) + (i32.add + (i32.add + (i32.add + (get_local $0) + (call $lb + (i32.const 3) + ) + ) + (block + (call $lb + (i32.const 4) + ) + (call $lb + (i32.const 5) + ) + (call $lb + (i32.const 6) + ) + (call $lb + (i32.const 7) + ) + ) + ) + (block + (call $lb + (i32.const 8) + ) + (call $lb + (i32.const 9) + ) + (call $lb + (i32.const 10) + ) + (call $lb + (i32.const 11) + ) + (call $lb + (i32.const 12) + ) + ) + ) + ) + (func $lb (param $0 i32) (result i32) (i32.store (get_local $0) (i32.add @@ -368,6 +429,7 @@ (i32.const 8) ) ) + (i32.const 0) ) (func $z (nop) diff --git a/test/unit.fromasm.imprecise.no-opts b/test/unit.fromasm.imprecise.no-opts index 4b765e4dcaa..70f8d286bc4 100644 --- a/test/unit.fromasm.imprecise.no-opts +++ b/test/unit.fromasm.imprecise.no-opts @@ -562,7 +562,114 @@ ) ) ) - (func $lb (param $a i32) + (func $recursiveBlockMerging (param $x i32) (result i32) + (call $lb + (i32.add + (i32.add + (i32.add + (block + (i32.const 1) + (get_local $x) + ) + (block + (i32.const 2) + (i32.const 3) + ) + ) + (block + (block + (block + (i32.const 4) + (i32.const 5) + ) + (i32.const 6) + ) + (i32.const 7) + ) + ) + (block + (i32.const 8) + (block + (i32.const 9) + (block + (i32.const 10) + (block + (i32.const 11) + (i32.const 12) + ) + ) + ) + ) + ) + ) + (set_local $x + (i32.add + (i32.add + (i32.add + (block + (call $lb + (i32.const 1) + ) + (get_local $x) + ) + (block + (call $lb + (i32.const 2) + ) + (call $lb + (i32.const 3) + ) + ) + ) + (block + (block + (block + (call $lb + (i32.const 4) + ) + (call $lb + (i32.const 5) + ) + ) + (call $lb + (i32.const 6) + ) + ) + (call $lb + (i32.const 7) + ) + ) + ) + (block + (call $lb + (i32.const 8) + ) + (block + (call $lb + (i32.const 9) + ) + (block + (call $lb + (i32.const 10) + ) + (block + (call $lb + (i32.const 11) + ) + (call $lb + (i32.const 12) + ) + ) + ) + ) + ) + ) + ) + (return + (get_local $x) + ) + ) + (func $lb (param $a i32) (result i32) (i32.store (get_local $a) (i32.add @@ -575,7 +682,9 @@ (i32.const 8) ) ) - (return) + (return + (i32.const 0) + ) ) (func $z (nop) diff --git a/test/unit.fromasm.no-opts b/test/unit.fromasm.no-opts index ddfa055779f..07311b00e20 100644 --- a/test/unit.fromasm.no-opts +++ b/test/unit.fromasm.no-opts @@ -566,7 +566,114 @@ ) ) ) - (func $lb (param $a i32) + (func $recursiveBlockMerging (param $x i32) (result i32) + (call $lb + (i32.add + (i32.add + (i32.add + (block + (i32.const 1) + (get_local $x) + ) + (block + (i32.const 2) + (i32.const 3) + ) + ) + (block + (block + (block + (i32.const 4) + (i32.const 5) + ) + (i32.const 6) + ) + (i32.const 7) + ) + ) + (block + (i32.const 8) + (block + (i32.const 9) + (block + (i32.const 10) + (block + (i32.const 11) + (i32.const 12) + ) + ) + ) + ) + ) + ) + (set_local $x + (i32.add + (i32.add + (i32.add + (block + (call $lb + (i32.const 1) + ) + (get_local $x) + ) + (block + (call $lb + (i32.const 2) + ) + (call $lb + (i32.const 3) + ) + ) + ) + (block + (block + (block + (call $lb + (i32.const 4) + ) + (call $lb + (i32.const 5) + ) + ) + (call $lb + (i32.const 6) + ) + ) + (call $lb + (i32.const 7) + ) + ) + ) + (block + (call $lb + (i32.const 8) + ) + (block + (call $lb + (i32.const 9) + ) + (block + (call $lb + (i32.const 10) + ) + (block + (call $lb + (i32.const 11) + ) + (call $lb + (i32.const 12) + ) + ) + ) + ) + ) + ) + ) + (return + (get_local $x) + ) + ) + (func $lb (param $a i32) (result i32) (i32.store (get_local $a) (i32.add @@ -579,7 +686,9 @@ (i32.const 8) ) ) - (return) + (return + (i32.const 0) + ) ) (func $z (nop)