diff --git a/auto_update_tests.py b/auto_update_tests.py index 49dce464e48..e62f42f0193 100755 --- a/auto_update_tests.py +++ b/auto_update_tests.py @@ -5,8 +5,10 @@ from scripts.test.support import run_command, split_wast from scripts.test.shared import ( ASM2WASM, MOZJS, S2WASM, WASM_SHELL, WASM_OPT, WASM_AS, WASM_DIS, - WASM_CTOR_EVAL, WASM_MERGE, WASM_REDUCE, + WASM_CTOR_EVAL, WASM_MERGE, WASM_REDUCE, WASM2ASM, BINARYEN_INSTALL_DIR, has_shell_timeout) +from scripts.test.wasm2asm import tests, spec_tests, extra_tests + print '[ processing and updating testcases... ]\n' @@ -261,6 +263,24 @@ out = t + '.out' with open(out, 'w') as o: o.write(actual) +print '\n[ checking wasm2asm ]\n' + +for wasm in tests + spec_tests + extra_tests: + if not wasm.endswith('.wast'): + continue + + asm = os.path.basename(wasm).replace('.wast', '.2asm.js') + expected_file = os.path.join('test', asm) + + if not os.path.exists(expected_file): + continue + + print '..', wasm + + cmd = WASM2ASM + [os.path.join('test', wasm)] + out = run_command(cmd) + with open(expected_file, 'w') as o: o.write(out) + if has_shell_timeout(): print '\n[ checking wasm-reduce ]\n' diff --git a/src/ast/branch-utils.h b/src/ast/branch-utils.h index 77ec7075326..05ead8571fa 100644 --- a/src/ast/branch-utils.h +++ b/src/ast/branch-utils.h @@ -103,11 +103,11 @@ inline std::set getBranchTargets(Expression* ast) { // Finds if there are branches targeting a name. Note that since names are // unique in our IR, we just need to look for the name, and do not need // to analyze scoping. -// By default we ignore untaken branches. You can set named to -// note those as well, then any named branch is noted, even if untaken +// By default we consider untaken branches (so any named use). You can unset named to +// avoid that (and only note branches that are not obviously unreachable) struct BranchSeeker : public PostWalker { Name target; - bool named = false; + bool named = true; Index found; WasmType valueType; @@ -144,16 +144,18 @@ struct BranchSeeker : public PostWalker { if (curr->default_ == target) noteFound(curr->value); } - static bool has(Expression* tree, Name target) { + static bool hasTaken(Expression* tree, Name target) { if (!target.is()) return false; BranchSeeker seeker(target); + seeker.named = false; seeker.walk(tree); return seeker.found > 0; } - static Index count(Expression* tree, Name target) { + static Index countTaken(Expression* tree, Name target) { if (!target.is()) return 0; BranchSeeker seeker(target); + seeker.named = false; seeker.walk(tree); return seeker.found; } @@ -161,7 +163,6 @@ struct BranchSeeker : public PostWalker { static bool hasNamed(Expression* tree, Name target) { if (!target.is()) return false; BranchSeeker seeker(target); - seeker.named = true; seeker.walk(tree); return seeker.found > 0; } @@ -169,7 +170,6 @@ struct BranchSeeker : public PostWalker { static Index countNamed(Expression* tree, Name target) { if (!target.is()) return 0; BranchSeeker seeker(target); - seeker.named = true; seeker.walk(tree); return seeker.found; } diff --git a/src/ast/type-updating.h b/src/ast/type-updating.h index dc0ae0f36f6..3cf2f2afe7c 100644 --- a/src/ast/type-updating.h +++ b/src/ast/type-updating.h @@ -132,13 +132,9 @@ struct TypeUpdater : public ExpressionStackWalkerdynCast()) { - if (BranchUtils::isBranchTaken(br)) { - noteBreakChange(br->name, change, br->value); - } + noteBreakChange(br->name, change, br->value); } else if (auto* sw = curr->dynCast()) { - if (BranchUtils::isBranchTaken(sw)) { - applySwitchChanges(sw, change); - } + applySwitchChanges(sw, change); } } @@ -200,34 +196,17 @@ struct TypeUpdater : public ExpressionStackWalkerdynCast()) { - 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()) { - 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 + // but exceptions exist if (auto* block = curr->dynCast()) { + // if the block has a fallthrough, it can keep its type + if (isConcreteWasmType(block->list.back()->type)) { + return; // did not turn + } // if the block has breaks, it can keep its type if (!block->name.is() || blockInfos[block->name].numBreaks == 0) { curr->type = unreachable; @@ -255,7 +234,7 @@ struct TypeUpdater : public ExpressionStackWalkername.is() && blockInfos[curr->name].numBreaks > 0) { - return;// has a break, not unreachable + return; // has a break, not unreachable } // look for a fallthrough makeBlockUnreachableIfNoFallThrough(curr); @@ -265,9 +244,13 @@ struct TypeUpdater : public ExpressionStackWalkertype == unreachable) { return; // no change possible } + if (!curr->list.empty() && + isConcreteWasmType(curr->list.back()->type)) { + return; // should keep type due to fallthrough, even if has an unreachable child + } for (auto* child : curr->list) { if (child->type == unreachable) { - // no fallthrough, this block is now unreachable + // no fallthrough, and an unreachable, => this block is now unreachable changeTypeTo(curr, unreachable); return; } diff --git a/src/ast_utils.h b/src/ast_utils.h index 1f781b87e50..852cf89a341 100644 --- a/src/ast_utils.h +++ b/src/ast_utils.h @@ -62,7 +62,7 @@ struct ExpressionAnalyzer { if (auto* br = curr->dynCast()) { if (!br->condition) return true; } else if (auto* block = curr->dynCast()) { - if (block->list.size() > 0 && obviouslyDoesNotFlowOut(block->list.back()) && !BranchUtils::BranchSeeker::has(block, block->name)) return true; + if (block->list.size() > 0 && obviouslyDoesNotFlowOut(block->list.back()) && !BranchUtils::BranchSeeker::hasTaken(block, block->name)) return true; } return false; } @@ -101,49 +101,59 @@ struct ReFinalize : public WalkerPass> { std::map breakValues; void visitBlock(Block *curr) { + if (curr->list.size() == 0) { + curr->type = none; + return; + } // do this quickly, without any validation + auto old = curr->type; + // last element determines type + curr->type = curr->list.back()->type; + // if concrete, it doesn't matter if we have an unreachable child, and we + // don't need to look at breaks + if (isConcreteWasmType(curr->type)) return; + // otherwise, we have no final fallthrough element to determine the type, + // could be determined by breaks if (curr->name.is()) { auto iter = breakValues.find(curr->name); if (iter != breakValues.end()) { // there is a break to here - curr->type = iter->second; + auto type = iter->second; + if (type == unreachable) { + // all we have are breaks with values of type unreachable, and no + // concrete fallthrough either. we must have had an existing type, then + curr->type = old; + assert(isConcreteWasmType(curr->type)); + } else { + curr->type = type; + } 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) + if (curr->type == unreachable) return; + // type is none, but we might be unreachable + if (curr->type == none) { for (auto* child : curr->list) { if (child->type == unreachable) { curr->type = unreachable; - return; + break; } } - // 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 (BranchUtils::isBranchTaken(curr)) { - breakValues[curr->name] = getValueType(curr->value); - } + updateBreakValueType(curr->name, getValueType(curr->value)); } void visitSwitch(Switch *curr) { curr->finalize(); - if (BranchUtils::isBranchTaken(curr)) { - auto valueType = getValueType(curr->value); - for (auto target : curr->targets) { - breakValues[target] = valueType; - } - breakValues[curr->default_] = valueType; + auto valueType = getValueType(curr->value); + for (auto target : curr->targets) { + updateBreakValueType(target, valueType); } + updateBreakValueType(curr->default_, valueType); } void visitCall(Call *curr) { curr->finalize(); } void visitCallImport(CallImport *curr) { curr->finalize(); } @@ -167,7 +177,13 @@ struct ReFinalize : public WalkerPass> { void visitUnreachable(Unreachable *curr) { curr->finalize(); } WasmType getValueType(Expression* value) { - return value && value->type != unreachable ? value->type : none; + return value ? value->type : none; + } + + void updateBreakValueType(Name name, WasmType type) { + if (type != unreachable || breakValues.count(name) == 0) { + breakValues[name] = type; + } } }; diff --git a/src/passes/FlattenControlFlow.cpp b/src/passes/FlattenControlFlow.cpp index 3da5809c3ff..dce8e634580 100644 --- a/src/passes/FlattenControlFlow.cpp +++ b/src/passes/FlattenControlFlow.cpp @@ -61,7 +61,7 @@ #include #include #include - +#include namespace wasm { @@ -461,6 +461,11 @@ struct FlattenControlFlow : public WalkerPass> { splitter.note(operand); } } + + void visitFunction(Function* curr) { + // removing breaks can alter types + ReFinalize().walkFunctionInModule(curr, getModule()); + } }; Pass *createFlattenControlFlowPass() { diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index f458a58b2f4..acbf3944709 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -397,6 +397,14 @@ struct OptimizeInstructions : public WalkerPasstype == unreachable && + !curr->is() && !curr->is() && !curr->is()) { + return nullptr; + } if (auto* binary = curr->dynCast()) { if (Properties::isSymmetric(binary)) { // canonicalize a const to the second position diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp index e307ec414f5..ec7809f4866 100644 --- a/src/passes/RemoveUnusedBrs.cpp +++ b/src/passes/RemoveUnusedBrs.cpp @@ -394,7 +394,9 @@ struct RemoveUnusedBrs : public WalkerPass> { if (list.size() == 1 && curr->name.is()) { // if this block has just one child, a sub-block, then jumps to the former are jumps to us, really if (auto* child = list[0]->dynCast()) { - if (child->name.is() && child->name != curr->name) { + // the two blocks must have the same type for us to update the branch, as otherwise + // one block may be unreachable and the other concrete, so one might lack a value + if (child->name.is() && child->name != curr->name && child->type == curr->type) { auto& breaks = breaksToBlock[child]; for (auto* br : breaks) { newNames[br] = curr->name; diff --git a/src/passes/SimplifyLocals.cpp b/src/passes/SimplifyLocals.cpp index 2d4a02337fd..919784cdf0b 100644 --- a/src/passes/SimplifyLocals.cpp +++ b/src/passes/SimplifyLocals.cpp @@ -372,8 +372,9 @@ struct SimplifyLocals : public WalkerPass> // optimize set_locals from both sides of an if into a return value void optimizeIfReturn(If* iff, Expression** currp, Sinkables& ifTrue) { assert(iff->ifFalse); - // if this if already has a result, we can't do anything - if (isConcreteWasmType(iff->type)) return; + // if this if already has a result, or is unreachable code, we have + // nothing to do + if (iff->type != none) return; // We now have the sinkables from both sides of the if. Sinkables& ifFalse = sinkables; Index sharedIndex = -1; diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index e30661e8f52..8772325216e 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -70,9 +70,7 @@ void WasmValidator::visitBlock(Block *curr) { if (curr->list.size() > 0) { auto backType = curr->list.back()->type; if (!isConcreteWasmType(curr->type)) { - if (isConcreteWasmType(backType)) { - shouldBeTrue(curr->type == unreachable, curr, "block with no value and a last element with a value must be unreachable"); - } + shouldBeFalse(isConcreteWasmType(backType), curr, "if block is not returning a value, final element should not flow out a value"); } else { if (isConcreteWasmType(backType)) { shouldBeEqual(curr->type, backType, curr, "block with value and last element with value must match types"); @@ -118,15 +116,19 @@ void WasmValidator::visitIf(If *curr) { shouldBeEqual(curr->ifFalse->type, unreachable, curr, "unreachable if-else must have unreachable false"); } } + if (isConcreteWasmType(curr->ifTrue->type)) { + shouldBeEqual(curr->type, curr->ifTrue->type, curr, "if type must match concrete ifTrue"); + shouldBeEqualOrFirstIsUnreachable(curr->ifFalse->type, curr->ifTrue->type, curr, "other arm must match concrete ifTrue"); + } + if (isConcreteWasmType(curr->ifFalse->type)) { + shouldBeEqual(curr->type, curr->ifFalse->type, curr, "if type must match concrete ifFalse"); + shouldBeEqualOrFirstIsUnreachable(curr->ifTrue->type, curr->ifFalse->type, curr, "other arm must match concrete ifFalse"); + } } } void WasmValidator::noteBreak(Name name, Expression* value, Expression* curr) { - if (!BranchUtils::isBranchTaken(curr)) { - // if not actually taken, just note the name - namedBreakTargets.insert(name); - return; - } + namedBreakTargets.insert(name); WasmType valueType = none; Index arity = 0; if (value) { @@ -548,7 +550,7 @@ void WasmValidator::visitFunction(Function *curr) { if (returnType != unreachable) { shouldBeEqual(curr->result, returnType, curr->body, "function result must match, if function has returns"); } - if (!shouldBeTrue(namedBreakTargets.empty(), curr->body, "all named break targets must exist (even if not taken)") && !quiet) { + if (!shouldBeTrue(namedBreakTargets.empty(), curr->body, "all named break targets must exist") && !quiet) { std::cerr << "(on label " << *namedBreakTargets.begin() << ")\n"; } returnType = unreachable; diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 4594eddd15c..a781e0fa3ee 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -116,13 +116,12 @@ struct TypeSeeker : public PostWalker { } void visitBreak(Break* curr) { - if (curr->name == targetName && BranchUtils::isBranchTaken(curr)) { + if (curr->name == targetName) { types.push_back(curr->value ? curr->value->type : none); } } void visitSwitch(Switch* curr) { - if (!BranchUtils::isBranchTaken(curr)) return; for (auto name : curr->targets) { if (name == targetName) types.push_back(curr->value ? curr->value->type : none); } @@ -174,16 +173,17 @@ static WasmType mergeTypes(std::vector& types) { // and there are no branches to it static void handleUnreachable(Block* block) { if (block->type == unreachable) return; // nothing to do + if (block->list.size() == 0) return; // nothing to do + // if we are concrete, stop - even an unreachable child + // won't change that (since we have a break with a value, + // or the final child flows out a value) + if (isConcreteWasmType(block->type)) return; + // look for an unreachable child for (auto* child : block->list) { if (child->type == unreachable) { // there is an unreachable child, so we are unreachable, unless we have a break - BranchUtils::BranchSeeker seeker(block->name); - Expression* expr = block; - seeker.walk(expr); - if (!seeker.found) { + if (!BranchUtils::BranchSeeker::hasNamed(block, block->name)) { block->type = unreachable; - } else { - block->type = seeker.valueType; } return; } @@ -199,19 +199,27 @@ void Block::finalize(WasmType type_) { void Block::finalize() { if (!name.is()) { - // nothing branches here, so this is easy if (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) + // nothing branches here, so this is easy + // normally the type is the type of the final child + type = list.back()->type; + // and even if we have an unreachable child somewhere, + // we still mark ourselves as having that type, + // (block (result i32) + // (return) + // (i32.const 10) + // ) + if (isConcreteWasmType(type)) return; + // if we are unreachable, we are done + if (type == unreachable) return; + // we may still be unreachable if we have an unreachable + // child for (auto* child : list) { if (child->type == unreachable) { type = unreachable; return; } } - // children are reachable, so last element determines type - type = list.back()->type; } else { type = none; } @@ -231,9 +239,7 @@ void If::finalize(WasmType type_) { } void If::finalize() { - if (condition->type == unreachable) { - type = unreachable; - } else if (ifFalse) { + if (ifFalse) { if (ifTrue->type == ifFalse->type) { type = ifTrue->type; } else if (isConcreteWasmType(ifTrue->type) && ifFalse->type == unreachable) { @@ -246,6 +252,17 @@ void If::finalize() { } else { type = none; // if without else } + // if the arms return a value, leave it even if the condition + // is unreachable, we still mark ourselves as having that type, e.g. + // (if (result i32) + // (unreachable) + // (i32.const 10) + // (i32.const 20 + // ) + // otherwise, if the condition is unreachable, so is the if + if (type == none && condition->type == unreachable) { + type = unreachable; + } } void Loop::finalize(WasmType type_) { diff --git a/test/br_table_temp.2asm.js b/test/br_table_temp.2asm.js index 19fd6736878..eb513b07152 100644 --- a/test/br_table_temp.2asm.js +++ b/test/br_table_temp.2asm.js @@ -49531,7 +49531,7 @@ function asmFunc(global, env, buffer) { } function $$20() { - var $$0 = 0, $$1 = 0; + var $$0 = 0, $$1 = 0, $$2 = 0, $$3 = 0; fake_return_waka123 : { loop_in : do { $$0 = 3; @@ -49549,7 +49549,7 @@ function asmFunc(global, env, buffer) { } function $$21() { - var $$0 = 0, $$1 = 0; + var $$0 = 0, $$1 = 0, $$2 = 0, $$3 = 0; fake_return_waka123 : { loop_in : do { dummy(); @@ -49677,7 +49677,7 @@ function asmFunc(global, env, buffer) { } function $$31() { - var $$0 = 0, $$1 = 0, $$2 = 0; + var $$0 = 0, $$1 = 0, $$2 = 0, $$3 = 0; $$if : { $$0 = 2; $$1 = $$0; @@ -49686,8 +49686,8 @@ function asmFunc(global, env, buffer) { break $$if; }; }; - $$2 = $$1; - return $$2 | 0; + $$3 = $$1; + return $$3 | 0; } function $$32($$0, $$1) { diff --git a/test/merge/basics.wast.combined.finalized.opt b/test/merge/basics.wast.combined.finalized.opt index 119851fb05b..7f57d67da2c 100644 --- a/test/merge/basics.wast.combined.finalized.opt +++ b/test/merge/basics.wast.combined.finalized.opt @@ -68,8 +68,8 @@ (nop) (drop (i32.add - (unreachable) (i32.const 14) + (unreachable) ) ) (nop) diff --git a/test/passes/flatten-control-flow.txt b/test/passes/flatten-control-flow.txt index d94536712b3..65e7196fb87 100644 --- a/test/passes/flatten-control-flow.txt +++ b/test/passes/flatten-control-flow.txt @@ -1174,4 +1174,11 @@ (get_local $2) ) ) + (func $switch-unreachable (type $1) + (block $label$3 + (block + (unreachable) + ) + ) + ) ) diff --git a/test/passes/flatten-control-flow.wast b/test/passes/flatten-control-flow.wast index 69f3146c037..97ecf74c784 100644 --- a/test/passes/flatten-control-flow.wast +++ b/test/passes/flatten-control-flow.wast @@ -773,4 +773,11 @@ (i32.const 1) ) ) + (func $switch-unreachable + (block $label$3 + (br_table $label$3 + (unreachable) + ) + ) + ) ) diff --git a/test/passes/merge-blocks.txt b/test/passes/merge-blocks.txt index f9ca7c3c108..be6906f0ac4 100644 --- a/test/passes/merge-blocks.txt +++ b/test/passes/merge-blocks.txt @@ -77,16 +77,12 @@ ) (func $drop-unreachable-br_if (type $2) (result i32) (block $label$0 (result i32) - (drop - (block $label$2 - (drop - (br_if $label$2 - (br $label$0 - (i32.const 538976371) - ) - (i32.const 1918987552) - ) + (block $label$2 (result i32) + (br_if $label$2 + (br $label$0 + (i32.const 538976371) ) + (i32.const 1918987552) ) ) ) diff --git a/test/passes/merge-blocks.wast b/test/passes/merge-blocks.wast index d7361273538..7cba9bf903e 100644 --- a/test/passes/merge-blocks.wast +++ b/test/passes/merge-blocks.wast @@ -55,16 +55,12 @@ ) (func $drop-unreachable-br_if (result i32) (block $label$0 (result i32) - (drop - (block $label$2 - (drop - (br_if $label$2 - (br $label$0 - (i32.const 538976371) - ) - (i32.const 1918987552) - ) + (block $label$2 (result i32) + (br_if $label$2 + (br $label$0 + (i32.const 538976371) ) + (i32.const 1918987552) ) ) ) diff --git a/test/passes/optimize-instructions.txt b/test/passes/optimize-instructions.txt index 55501dfb871..6a99ee10641 100644 --- a/test/passes/optimize-instructions.txt +++ b/test/passes/optimize-instructions.txt @@ -355,8 +355,8 @@ ) (drop (i32.and - (unreachable) (i32.const 1) + (unreachable) ) ) (drop @@ -2064,8 +2064,11 @@ ) (func $shifts-square-unreachable (type $3) (param $x i32) (result i32) (i32.shr_u - (unreachable) - (i32.const 9) + (i32.shr_u + (unreachable) + (i32.const 1031) + ) + (i32.const 4098) ) ) (func $mix-shifts (type $2) (result i32) diff --git a/test/passes/optimize-instructions_optimize-level=2_ignore-implicit-traps.txt b/test/passes/optimize-instructions_optimize-level=2_ignore-implicit-traps.txt index 922f6400858..85e0dd6d8c9 100644 --- a/test/passes/optimize-instructions_optimize-level=2_ignore-implicit-traps.txt +++ b/test/passes/optimize-instructions_optimize-level=2_ignore-implicit-traps.txt @@ -1,5 +1,6 @@ (module (type $0 (func (param i32 i32) (result i32))) + (type $1 (func (result f64))) (memory $0 0) (func $conditionals (type $0) (param $0 i32) (param $1 i32) (result i32) (local $2 i32) @@ -321,4 +322,41 @@ (get_local $1) ) ) + (func $conditionalize-if-type-change (type $1) (result f64) + (local $0 i32) + (drop + (loop $label$1 (result f32) + (block $label$2 (result f32) + (drop + (block $label$3 (result f32) + (br_if $label$1 + (i32.or + (f32.gt + (br_if $label$3 + (f32.const 1) + (get_local $0) + ) + (br $label$2 + (f32.const 71) + ) + ) + (i64.eqz + (select + (i64.const 58) + (i64.const -982757) + (i64.eqz + (i64.const 0) + ) + ) + ) + ) + ) + ) + ) + (f32.const 1) + ) + ) + ) + (f64.const -nan:0xfffffffffffff) + ) ) diff --git a/test/passes/optimize-instructions_optimize-level=2_ignore-implicit-traps.wast b/test/passes/optimize-instructions_optimize-level=2_ignore-implicit-traps.wast index 7e83658126e..6e19255ed92 100644 --- a/test/passes/optimize-instructions_optimize-level=2_ignore-implicit-traps.wast +++ b/test/passes/optimize-instructions_optimize-level=2_ignore-implicit-traps.wast @@ -323,5 +323,43 @@ ) (return (get_local $1)) ) + + (func $conditionalize-if-type-change (result f64) + (local $0 i32) + (drop + (loop $label$1 (result f32) + (block $label$2 (result f32) + (drop + (block $label$3 (result f32) + (br_if $label$1 + (i32.or ;; this turns into an if, but then the if might not be unreachable + (f32.gt + (br_if $label$3 + (f32.const 1) + (get_local $0) + ) + (br $label$2 + (f32.const 71) + ) + ) + (i64.eqz + (select + (i64.const 58) + (i64.const -982757) + (i64.eqz + (i64.const 0) + ) + ) + ) + ) + ) + ) + ) + (f32.const 1) + ) + ) + ) + (f64.const -nan:0xfffffffffffff) + ) ) diff --git a/test/passes/precompute.txt b/test/passes/precompute.txt index 74e4989dcb3..ad5b74e8387 100644 --- a/test/passes/precompute.txt +++ b/test/passes/precompute.txt @@ -117,7 +117,7 @@ ) (func $br_if-condition-is-block-i32-but-unreachable-so-refinalize-tricky (type $2) (drop - (block $label$1 + (block $label$1 (result i32) (drop (br_if $label$1 (i32.const 100) @@ -157,4 +157,34 @@ (f64.const 4776014875438170098655851e156) ) ) + (func $refinalize-two-breaks-one-unreachable (type $2) + (drop + (block $label$0 (result i64) + (br_if $label$0 + (select + (i64.const 1) + (block $block + (set_global $global$0 + (i32.const 1) + ) + (br $label$0 + (i64.const -22) + ) + ) + (i32.const 0) + ) + (i32.const 1) + ) + ) + ) + ) + (func $one-break-value-and-it-is-unreachable (type $3) (result f64) + (local $var$0 i32) + (block $label$6 (result f64) + (br_if $label$6 + (unreachable) + (i32.const 0) + ) + ) + ) ) diff --git a/test/passes/precompute.wast b/test/passes/precompute.wast index 3eb2683bf7b..b339a55d446 100644 --- a/test/passes/precompute.wast +++ b/test/passes/precompute.wast @@ -249,4 +249,35 @@ (f64.const 4776014875438170098655851e156) ) ) + (func $refinalize-two-breaks-one-unreachable + (drop + (block $label$0 (result i64) + (br_if $label$0 + (select + (i64.const 1) + (block (result i64) + (set_global $global$0 + (i32.const 1) + ) + (br_if $label$0 + (i64.const -22) + (i32.const -1) + ) + ) + (i32.const 0) + ) + (i32.const 1) + ) + ) + ) + ) + (func $one-break-value-and-it-is-unreachable (result f64) + (local $var$0 i32) + (block $label$6 (result f64) + (br_if $label$6 + (unreachable) + (i32.const 0) + ) + ) + ) ) diff --git a/test/passes/remove-unused-brs.txt b/test/passes/remove-unused-brs.txt index 7951cf18f84..6991680f790 100644 --- a/test/passes/remove-unused-brs.txt +++ b/test/passes/remove-unused-brs.txt @@ -332,7 +332,7 @@ ) ) (if - (block $block6 + (block $block6 (result i32) (block $block15 (drop (i32.const 2) @@ -1037,11 +1037,23 @@ ) ) ) - (func $untaken-br-with-concrete-last-element (type $2) (result i32) - (block $label$8 (result i32) - (block $label$11 (result i32) + (func $untaken-br-with-concrete-last-element (type $1) + (block $label$8 + (block $label$11 (block $label$14 (br_if $label$8 + (br $label$8) + ) + ) + ) + ) + ) + (func $untaken-br-with-concrete-last-element2 (type $2) (result i32) + (block $label$8 (result i32) + (block $label$11 (result i32) + (block $label$14 (result i32) + (br_if $label$14 + (i32.const 102) (br $label$11 (i32.const 103) ) @@ -1062,25 +1074,29 @@ ) ) (func $unreachable-if-that-could-be-a-br_if (type $7) (result i64) - (loop $label$3 - (if - (unreachable) - (f64.const 1) - (br $label$3) + (loop $label$3 (result i64) + (drop + (if (result f64) + (unreachable) + (f64.const 1) + (br $label$3) + ) ) (i64.const 1) ) ) (func $nop-br-might-update-type (type $1) (block $label$39 - (if - (unreachable) + (drop (if (result i32) - (i32.const 1) - (br $label$39) + (unreachable) + (if (result i32) + (i32.const 1) + (br $label$39) + (i32.const 0) + ) (i32.const 0) ) - (i32.const 0) ) ) ) diff --git a/test/passes/remove-unused-brs.wast b/test/passes/remove-unused-brs.wast index 20441d9e41e..5c677622685 100644 --- a/test/passes/remove-unused-brs.wast +++ b/test/passes/remove-unused-brs.wast @@ -919,11 +919,24 @@ ) ) ) - (func $untaken-br-with-concrete-last-element (result i32) + (func $untaken-br-with-concrete-last-element + (block $label$8 + (block $label$11 + (block $label$14 + (br_if $label$14 + (br $label$11 + ) + ) + ) + ) + ) + ) + (func $untaken-br-with-concrete-last-element2 (result i32) (block $label$8 (result i32) (block $label$11 (result i32) - (block $label$14 + (block $label$14 (result i32) (br_if $label$14 + (i32.const 102) (br $label$11 (i32.const 103) ) @@ -944,25 +957,29 @@ ) ) (func $unreachable-if-that-could-be-a-br_if (result i64) - (loop $label$3 - (if - (unreachable) - (f64.const 1) - (br $label$3) + (loop $label$3 (result i64) + (drop + (if (result f64) + (unreachable) + (f64.const 1) + (br $label$3) + ) ) (i64.const 1) ) ) (func $nop-br-might-update-type (block $label$39 - (if - (unreachable) + (drop (if (result i32) - (i32.const 1) - (br $label$39) ;; if we nop this, then the parent type must change + (unreachable) + (if (result i32) + (i32.const 1) + (br $label$39) ;; if we nop this, then the parent type must change + (i32.const 0) + ) (i32.const 0) ) - (i32.const 0) ) ) ) diff --git a/test/passes/remove-unused-names.txt b/test/passes/remove-unused-names.txt index 894e0716592..6fd0f4e5101 100644 --- a/test/passes/remove-unused-names.txt +++ b/test/passes/remove-unused-names.txt @@ -66,15 +66,13 @@ ) (func $merge-typed-with-unreachable-child (type $2) (result i32) (local $0 f32) - (block $label$0 (result i32) - (block $label$1 + (block $label$1 (result i32) + (br_if $label$1 + (i32.const 1) (br_if $label$1 (i32.const 0) - (br_if $label$0 + (br $label$1 (i32.const 0) - (br $label$0 - (i32.const 0) - ) ) ) ) diff --git a/test/passes/remove-unused-names.wast b/test/passes/remove-unused-names.wast index dc882cf4c27..a4c240f0270 100644 --- a/test/passes/remove-unused-names.wast +++ b/test/passes/remove-unused-names.wast @@ -80,9 +80,9 @@ (func $merge-typed-with-unreachable-child (result i32) (local $0 f32) (block $label$0 (result i32) - (block $label$1 + (block $label$1 (result i32) (br_if $label$1 - (i32.const 0) + (i32.const 1) (br_if $label$0 (i32.const 0) (br $label$0 diff --git a/test/passes/remove-unused-names_merge-blocks.txt b/test/passes/remove-unused-names_merge-blocks.txt index 19f03c662e2..c170a6c99a4 100644 --- a/test/passes/remove-unused-names_merge-blocks.txt +++ b/test/passes/remove-unused-names_merge-blocks.txt @@ -853,9 +853,11 @@ (unreachable) ) (func $concrete_finale_in_unreachable (type $5) (result f64) - (unreachable) (drop - (f64.const 6.322092475576799e-96) + (block (result f64) + (unreachable) + (f64.const 6.322092475576799e-96) + ) ) (f64.const -1) ) @@ -897,7 +899,7 @@ ) (func $drop-unreachable-block-with-concrete-final (type $3) (drop - (block + (block (result i32) (drop (block (drop diff --git a/test/passes/remove-unused-names_merge-blocks.wast b/test/passes/remove-unused-names_merge-blocks.wast index 6f6dd92b9e2..67a1f2762d0 100644 --- a/test/passes/remove-unused-names_merge-blocks.wast +++ b/test/passes/remove-unused-names_merge-blocks.wast @@ -1007,9 +1007,11 @@ ) (func $concrete_finale_in_unreachable (result f64) (block $label$0 (result f64) - (block ;; this block is unreachable - (unreachable) - (f64.const 6.322092475576799e-96) + (drop + (block (result f64) + (unreachable) + (f64.const 6.322092475576799e-96) + ) ) (f64.const -1) ) @@ -1056,7 +1058,7 @@ ) (func $drop-unreachable-block-with-concrete-final (drop - (block + (block (result i32) (drop (block (drop @@ -1070,7 +1072,7 @@ ) (func $merging-with-unreachable-in-middle (result i32) (block $label$1 (result i32) - (block + (block (result i32) (return (i32.const 21536) ) diff --git a/test/passes/simplify-locals.txt b/test/passes/simplify-locals.txt index 39fc1d97d67..15d8b21e5fc 100644 --- a/test/passes/simplify-locals.txt +++ b/test/passes/simplify-locals.txt @@ -877,17 +877,13 @@ ) ) (func $if-return-but-unreachable (type $10) (param $var$0 i64) - (tee_local $var$0 - (if - (unreachable) - (block (result i64) - (nop) - (get_local $var$0) - ) - (block (result i64) - (nop) - (i64.const 1) - ) + (if + (unreachable) + (set_local $var$0 + (get_local $var$0) + ) + (set_local $var$0 + (i64.const 1) ) ) ) diff --git a/test/passes/vacuum.txt b/test/passes/vacuum.txt index 011fb99f55f..b254179cddf 100644 --- a/test/passes/vacuum.txt +++ b/test/passes/vacuum.txt @@ -223,7 +223,7 @@ ) ) (func $leave-block-even-if-br-not-taken (type $6) (result f64) - (block $label$0 + (block $label$0 (result f64) (f64.store align=1 (i32.const 879179022) (br_if $label$0 @@ -276,12 +276,11 @@ (func $unreachable-if-with-nop-arm-that-leaves-a-concrete-value-if-nop-is-removed (type $0) (block $label$0 (loop $label$1 - (br_if $label$0 - (i32.load8_s - (i32.const 1634541608) - ) - (loop $label$9 - (br $label$9) + (drop + (br_if $label$0 + (loop $label$9 + (br $label$9) + ) ) ) ) diff --git a/test/passes/vacuum.wast b/test/passes/vacuum.wast index 3b5b22e9ce2..dbbffee41b2 100644 --- a/test/passes/vacuum.wast +++ b/test/passes/vacuum.wast @@ -502,7 +502,7 @@ (local $2 i32) (block $label$0 (drop - (block + (block (result i32) (br $label$0) (get_local $2) ) @@ -596,17 +596,16 @@ (func $unreachable-if-with-nop-arm-that-leaves-a-concrete-value-if-nop-is-removed (block $label$0 (loop $label$1 - (if - (br_if $label$0 - (i32.load8_s - (i32.const 1634541608) - ) - (loop $label$9 - (br $label$9) + (drop + (if (result i32) + (br_if $label$0 + (loop $label$9 + (br $label$9) + ) ) + (unreachable) + (i32.const 1920103026) ) - (nop) - (i32.const 1920103026) ) ) ) diff --git a/test/polymorphic_stack.wast b/test/polymorphic_stack.wast index 1b245914801..d0b9986baec 100644 --- a/test/polymorphic_stack.wast +++ b/test/polymorphic_stack.wast @@ -80,7 +80,8 @@ (func $untaken-break-should-have-value (result i32) (block $x (result i32) (block - (br_if $x ;; ok to not have a value, since an untaken branch. but must emit valid binary for wasm + (br_if $x + (i32.const 0) (unreachable) ) ) @@ -95,6 +96,7 @@ ) (block $label$0 (result i32) (br_if $label$0 + (i32.const 0) (return (i32.const -32) ) @@ -103,7 +105,7 @@ ) (func $br_table_unreachable_to_also_unreachable (result i32) (block $a (result i32) - (block $b + (block $b (result i32) (br_table $a $b ;; seems to send a value, but is not taken (unreachable) (unreachable) @@ -111,5 +113,21 @@ ) ) ) + (func $untaken-br_if (result i32) + (block $label$8 (result i32) + (block $label$9 + (drop + (if + (i32.const 0) + (br_if $label$8 + (unreachable) + (i32.const 0) + ) + (unreachable) + ) + ) + ) + ) + ) ) diff --git a/test/polymorphic_stack.wast.from-wast b/test/polymorphic_stack.wast.from-wast index 82f88f4af57..8b514d8f4e6 100644 --- a/test/polymorphic_stack.wast.from-wast +++ b/test/polymorphic_stack.wast.from-wast @@ -85,6 +85,7 @@ (block $x (result i32) (block $block (br_if $x + (i32.const 0) (unreachable) ) ) @@ -99,6 +100,7 @@ ) (block $label$0 (result i32) (br_if $label$0 + (i32.const 0) (return (i32.const -32) ) @@ -107,7 +109,7 @@ ) (func $br_table_unreachable_to_also_unreachable (type $1) (result i32) (block $a (result i32) - (block $b + (block $b (result i32) (br_table $a $b (unreachable) (unreachable) @@ -115,4 +117,20 @@ ) ) ) + (func $untaken-br_if (type $1) (result i32) + (block $label$8 (result i32) + (block $label$9 + (drop + (if + (i32.const 0) + (br_if $label$8 + (unreachable) + (i32.const 0) + ) + (unreachable) + ) + ) + ) + ) + ) ) diff --git a/test/polymorphic_stack.wast.fromBinary b/test/polymorphic_stack.wast.fromBinary index 68bb7ecf781..709d131c0e4 100644 --- a/test/polymorphic_stack.wast.fromBinary +++ b/test/polymorphic_stack.wast.fromBinary @@ -36,6 +36,9 @@ (func $untaken-break-should-have-value (type $1) (result i32) (block $label$0 (result i32) (block $label$1 + (drop + (i32.const 0) + ) (unreachable) ) (unreachable) @@ -52,6 +55,9 @@ ) ) (block $label$2 (result i32) + (drop + (i32.const 0) + ) (return (i32.const -32) ) @@ -60,9 +66,24 @@ ) (func $br_table_unreachable_to_also_unreachable (type $1) (result i32) (block $label$0 (result i32) - (block $label$1 + (block $label$1 (result i32) (unreachable) ) + ) + ) + (func $untaken-br_if (type $1) (result i32) + (block $label$0 (result i32) + (block $label$1 + (if + (i32.const 0) + (block $label$2 + (unreachable) + ) + (block $label$3 + (unreachable) + ) + ) + ) (unreachable) ) ) diff --git a/test/polymorphic_stack.wast.fromBinary.noDebugInfo b/test/polymorphic_stack.wast.fromBinary.noDebugInfo index 69292952e8d..3c0af42650c 100644 --- a/test/polymorphic_stack.wast.fromBinary.noDebugInfo +++ b/test/polymorphic_stack.wast.fromBinary.noDebugInfo @@ -36,6 +36,9 @@ (func $5 (type $1) (result i32) (block $label$0 (result i32) (block $label$1 + (drop + (i32.const 0) + ) (unreachable) ) (unreachable) @@ -52,6 +55,9 @@ ) ) (block $label$2 (result i32) + (drop + (i32.const 0) + ) (return (i32.const -32) ) @@ -60,9 +66,24 @@ ) (func $7 (type $1) (result i32) (block $label$0 (result i32) - (block $label$1 + (block $label$1 (result i32) (unreachable) ) + ) + ) + (func $8 (type $1) (result i32) + (block $label$0 (result i32) + (block $label$1 + (if + (i32.const 0) + (block $label$2 + (unreachable) + ) + (block $label$3 + (unreachable) + ) + ) + ) (unreachable) ) ) diff --git a/test/spec b/test/spec index 668281b3d7d..8b1077b8a9e 160000 --- a/test/spec +++ b/test/spec @@ -1 +1 @@ -Subproject commit 668281b3d7dfe9be6cbc3c4500b537c49d6449b9 +Subproject commit 8b1077b8a9e75054ec373b064a25fd23e9a39719 diff --git a/test/unit.wast b/test/unit.wast index a429675de44..c28e7b7f84d 100644 --- a/test/unit.wast +++ b/test/unit.wast @@ -545,7 +545,9 @@ ) (block (unreachable) - (i32.const 1) ;; ends in a concrete, after an unreachable + (drop + (i32.const 1) + ) ) ) ) diff --git a/test/unit.wast.from-wast b/test/unit.wast.from-wast index 7fb82d69d29..721b6c3d291 100644 --- a/test/unit.wast.from-wast +++ b/test/unit.wast.from-wast @@ -610,7 +610,9 @@ ) (block $block12 (unreachable) - (i32.const 1) + (drop + (i32.const 1) + ) ) ) ) diff --git a/test/untaken-br_if.wast b/test/untaken-br_if.wast index a165cf67fff..92ed74c959a 100644 --- a/test/untaken-br_if.wast +++ b/test/untaken-br_if.wast @@ -1,11 +1,11 @@ (module (func $binaryify-untaken-br_if (result f32) - (if + (if (result f32) (i32.const 1) (unreachable) - (block $label$1 + (block $label$1 (result f32) (br_if $label$1 - (i32.const 1) + (f32.const 1) (unreachable) ) ) diff --git a/test/untaken-br_if.wast.from-wast b/test/untaken-br_if.wast.from-wast index 2d6d9dd2d7b..86ce53f41b4 100644 --- a/test/untaken-br_if.wast.from-wast +++ b/test/untaken-br_if.wast.from-wast @@ -2,12 +2,12 @@ (type $0 (func (result f32))) (memory $0 0) (func $binaryify-untaken-br_if (type $0) (result f32) - (if + (if (result f32) (i32.const 1) (unreachable) - (block $label$1 + (block $label$1 (result f32) (br_if $label$1 - (i32.const 1) + (f32.const 1) (unreachable) ) ) diff --git a/test/untaken-br_if.wast.fromBinary b/test/untaken-br_if.wast.fromBinary index 815789c351a..fc2e7f215ac 100644 --- a/test/untaken-br_if.wast.fromBinary +++ b/test/untaken-br_if.wast.fromBinary @@ -2,15 +2,15 @@ (type $0 (func (result f32))) (memory $0 0) (func $binaryify-untaken-br_if (type $0) (result f32) - (if + (if (result f32) (i32.const 1) - (block $label$0 + (block $label$0 (result f32) (unreachable) ) - (block $label$1 - (block $label$2 + (block $label$1 (result f32) + (block $label$2 (result f32) (drop - (i32.const 1) + (f32.const 1) ) (unreachable) ) diff --git a/test/untaken-br_if.wast.fromBinary.noDebugInfo b/test/untaken-br_if.wast.fromBinary.noDebugInfo index 9b31417eb26..b15b0f0b4a0 100644 --- a/test/untaken-br_if.wast.fromBinary.noDebugInfo +++ b/test/untaken-br_if.wast.fromBinary.noDebugInfo @@ -2,15 +2,15 @@ (type $0 (func (result f32))) (memory $0 0) (func $0 (type $0) (result f32) - (if + (if (result f32) (i32.const 1) - (block $label$0 + (block $label$0 (result f32) (unreachable) ) - (block $label$1 - (block $label$2 + (block $label$1 (result f32) + (block $label$2 (result f32) (drop - (i32.const 1) + (f32.const 1) ) (unreachable) )