diff --git a/src/asm2wasm.h b/src/asm2wasm.h index eaf4f8c18eb..db2745ff730 100644 --- a/src/asm2wasm.h +++ b/src/asm2wasm.h @@ -565,6 +565,8 @@ class Asm2WasmBuilder { } Function* processFunction(Ref ast); + + void fixFallthrough(Function* func); }; void Asm2WasmBuilder::processAsm(Ref ast) { @@ -689,7 +691,7 @@ void Asm2WasmBuilder::processAsm(Ref ast) { passRunner.setValidateGlobally(false); } // run autodrop first, before optimizations - passRunner.add(); + passRunner.add(); // TODO: we can likely remove this, and speed up the build a little // optimize relooper label variable usage at the wasm level, where it is easy passRunner.add("relooper-jump-threading"); }, debug, false /* do not validate globally yet */); @@ -1507,8 +1509,9 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { abort_on("bad unary", ast); } else if (what == IF) { auto* condition = process(ast[1]); - auto* ifTrue = process(ast[2]); - return builder.makeIf(truncateToInt32(condition), ifTrue, !!ast[3] ? process(ast[3]) : nullptr); + auto* ifTrue = builder.dropIfConcretelyTyped(process(ast[2])); + auto* ifFalse = !!ast[3] ? builder.dropIfConcretelyTyped(process(ast[3])) : nullptr; + return builder.makeIf(truncateToInt32(condition), ifTrue, ifFalse); } else if (what == CALL) { if (ast[1]->isString()) { IString name = ast[1]->getIString(); @@ -1830,7 +1833,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { block = allocator.alloc(); block->name = name; block->list.push_back(ret); - block->finalize(); + block->finalize(none); ret = block; } } @@ -1874,7 +1877,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { auto body = allocator.alloc(); body->list.push_back(condition); body->list.push_back(process(ast[2])); - body->finalize(); + body->finalize(none); ret->body = body; } // loops do not automatically loop, add a branch back @@ -1882,8 +1885,8 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { auto continuer = allocator.alloc(); continuer->name = ret->name; block->list.push_back(continuer); - block->finalize(); - ret->body = block; + block->finalize(none); + ret->body = builder.dropIfConcretelyTyped(block); ret->finalize(); continueStack.pop_back(); breakStack.pop_back(); @@ -1904,7 +1907,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { Name more = nameMapper.pushLabelName("unlikely-continue"); breakStack.push_back(stop); continueStack.push_back(more); - auto child = process(ast[2]); + auto child = builder.dropIfConcretelyTyped(process(ast[2])); continueStack.pop_back(); breakStack.pop_back(); nameMapper.popLabelName(more); @@ -1919,13 +1922,13 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { block->list.push_back(builder.makeNop()); // ensure a nop at the end, so the block has guaranteed none type and no values fall through } block->name = stop; - block->finalize(); + block->finalize(none); return block; } else { auto loop = allocator.alloc(); loop->body = child; loop->name = more; - loop->finalize(); + loop->finalize(none); return builder.blockifyWithName(loop, stop); } } @@ -1945,7 +1948,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { loop->name = in; breakStack.push_back(out); continueStack.push_back(in); - loop->body = process(ast[2]); + loop->body = builder.dropIfConcretelyTyped(process(ast[2])); continueStack.pop_back(); breakStack.pop_back(); nameMapper.popLabelName(in); @@ -1955,7 +1958,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { continuer->condition = process(ast[1]); Block *block = builder.blockifyWithName(loop->body, out, continuer); loop->body = block; - loop->finalize(); + loop->finalize(none); return loop; } else if (what == FOR) { Ref finit = ast[1], @@ -1987,8 +1990,8 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { body->list.push_back(condition); body->list.push_back(process(fbody)); body->list.push_back(process(finc)); - body->finalize(); - ret->body = body; + body->finalize(none); + ret->body = builder.dropIfConcretelyTyped(body); // loops do not automatically loop, add a branch back auto continuer = allocator.alloc(); continuer->name = ret->name; @@ -2003,7 +2006,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { // add an outer block for the init as well outer->list.push_back(process(finit)); outer->list.push_back(ret); - outer->finalize(); + outer->finalize(none); return outer; } else if (what == LABEL) { assert(parentLabel.isNull()); @@ -2139,7 +2142,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { } top->list.push_back(br); - top->finalize(); + top->finalize(none); for (unsigned i = 0; i < cases->size(); i++) { Ref curr = cases[i]; @@ -2163,9 +2166,9 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { } auto next = allocator.alloc(); top->name = name; - next->list.push_back(top); - next->list.push_back(case_); - next->finalize(); + next->list.push_back(builder.dropIfConcretelyTyped(top)); + next->list.push_back(builder.dropIfConcretelyTyped(case_)); + next->finalize(none); top = next; nameMapper.popLabelName(name); } @@ -2212,9 +2215,9 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { } auto next = allocator.alloc(); top->name = name; - next->list.push_back(top); - next->list.push_back(case_); - next->finalize(); + next->list.push_back(builder.dropIfConcretelyTyped(top)); + next->list.push_back(builder.dropIfConcretelyTyped(case_)); + next->finalize(none); top = next; nameMapper.popLabelName(name); } @@ -2230,7 +2233,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { first->ifFalse = builder.makeBreak(br->default_); brHolder->list.push_back(chain); - brHolder->finalize(); + brHolder->finalize(none); } breakStack.pop_back(); @@ -2268,6 +2271,8 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { for (unsigned i = from; i < ast->size(); i++) { block->list.push_back(process(ast[i])); } + // if the last element has a value, we must drop it - a list of statements never falls through in asm.js + block->list.back() = builder.dropIfConcretelyTyped(block->list.back()); block->finalize(); return block; }; @@ -2276,9 +2281,30 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { // cleanups/checks assert(breakStack.size() == 0 && continueStack.size() == 0); assert(parentLabel.isNull()); + // fix up the fallthrough return value. asm2wasm output is structured statements, + // but the wasm toplevel scope is a fallthrough that must have the same type as + // the function return value, if there is one. We must propage that type up + // as far as necessary, replacing unreachable types with concrete ones, e.g. + // + // block + // if + // condition + // return 1 + // return 2 + // + // then naively they all have unreachable type, but if the function returns i32, + // the block *and* the if must be i32 + fixFallthrough(function); return function; } +void Asm2WasmBuilder::fixFallthrough(Function* func) { + if (func->result == none) return; + Block* block = builder.blockify(func->body); + block->finalize(func->result); + func->body = block; +} + } // namespace wasm #endif // wasm_asm2wasm_h diff --git a/src/ast_utils.h b/src/ast_utils.h index 2a151d8f71a..1221c5b9467 100644 --- a/src/ast_utils.h +++ b/src/ast_utils.h @@ -270,6 +270,10 @@ struct ExpressionAnalyzer { return !curr->condition && !curr->value; } + static bool isControlFlowStructure(Expression* curr) { + return curr->is() || curr->is() || curr->is(); + } + // Checks if an expression does not flow out in an obvious way. // We return true if it cannot flow out. If it can flow out, we // might still return true, as the analysis here is simple and fast. @@ -335,26 +339,6 @@ struct AutoDrop : public WalkerPasstype)) { - expressionStack.push_back(child); - if (!ExpressionAnalyzer::isResultUsed(expressionStack, getFunction()) && !ExpressionAnalyzer::isResultDropped(expressionStack)) { - child = Builder(*getModule()).makeDrop(child); - acted = true; - } - expressionStack.pop_back(); - } - return acted; - } - - void reFinalize() { - for (int i = int(expressionStack.size()) - 1; i >= 0; i--) { - auto* curr = expressionStack[i]; - ReFinalize().visit(curr); - } - } - void visitBlock(Block* curr) { if (curr->list.size() == 0) return; for (Index i = 0; i < curr->list.size() - 1; i++) { @@ -363,21 +347,16 @@ struct AutoDrop : public WalkerPasslist[i] = Builder(*getModule()).makeDrop(child); } } - if (maybeDrop(curr->list.back())) { - reFinalize(); - assert(curr->type == none); - } } void visitIf(If* curr) { - bool acted = false; - if (maybeDrop(curr->ifTrue)) acted = true; - if (curr->ifFalse) { - if (maybeDrop(curr->ifFalse)) acted = true; + if (isConcreteWasmType(curr->type)) return; // ok to return a value, no need to drop + // this is an if without an else, or an if-else with no return value - drop any values + if (isConcreteWasmType(curr->ifTrue->type)) { + curr->ifTrue = Builder(*getModule()).makeDrop(curr->ifTrue); } - if (acted) { - reFinalize(); - assert(curr->type == none); + if (curr->ifFalse && isConcreteWasmType(curr->ifFalse->type)) { + curr->ifFalse = Builder(*getModule()).makeDrop(curr->ifFalse); } } diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index c618f9d1448..b76234ed824 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -299,13 +299,13 @@ BinaryenOp BinaryenCurrentMemory(void) { return CurrentMemory; } BinaryenOp BinaryenGrowMemory(void) { return GrowMemory; } BinaryenOp BinaryenHasFeature(void) { return HasFeature; } -BinaryenExpressionRef BinaryenBlock(BinaryenModuleRef module, const char* name, BinaryenExpressionRef* children, BinaryenIndex numChildren) { +BinaryenExpressionRef BinaryenBlock(BinaryenModuleRef module, const char* name, BinaryenExpressionRef* children, BinaryenIndex numChildren, BinaryenType resultType) { auto* ret = ((Module*)module)->allocator.alloc(); if (name) ret->name = name; for (BinaryenIndex i = 0; i < numChildren; i++) { ret->list.push_back((Expression*)children[i]); } - ret->finalize(); + ret->finalize(WasmType(resultType)); if (tracing) { std::cout << " {\n"; @@ -319,7 +319,7 @@ BinaryenExpressionRef BinaryenBlock(BinaryenModuleRef module, const char* name, auto id = noteExpression(ret); std::cout << " expressions[" << id << "] = BinaryenBlock(the_module, "; traceNameOrNULL(name); - std::cout << ", children, " << numChildren << ");\n"; + std::cout << ", children, " << numChildren << ", " << resultType << ");\n"; std::cout << " }\n"; } diff --git a/src/binaryen-c.h b/src/binaryen-c.h index 9ecb3f40936..26503548d2d 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -261,8 +261,8 @@ BinaryenOp BinaryenHasFeature(void); typedef void* BinaryenExpressionRef; -// Block: name can be NULL -BinaryenExpressionRef BinaryenBlock(BinaryenModuleRef module, const char* name, BinaryenExpressionRef* children, BinaryenIndex numChildren); +// Block: name can be NULL. type must be explicitly provided, per the wasm type rules +BinaryenExpressionRef BinaryenBlock(BinaryenModuleRef module, const char* name, BinaryenExpressionRef* children, BinaryenIndex numChildren, BinaryenType resultType); // If: ifFalse can be NULL BinaryenExpressionRef BinaryenIf(BinaryenModuleRef module, BinaryenExpressionRef condition, BinaryenExpressionRef ifTrue, BinaryenExpressionRef ifFalse); BinaryenExpressionRef BinaryenLoop(BinaryenModuleRef module, const char* in, BinaryenExpressionRef body); diff --git a/src/mixed_arena.h b/src/mixed_arena.h index 52e47fbde4d..eeec46d2ded 100644 --- a/src/mixed_arena.h +++ b/src/mixed_arena.h @@ -171,6 +171,10 @@ class ArenaVectorBase { return usedElements; } + bool empty() const { + return size() == 0; + } + void resize(size_t size) { if (size > allocatedElements) { reallocate(size); diff --git a/src/pass.h b/src/pass.h index dc9159aa063..49d0627346e 100644 --- a/src/pass.h +++ b/src/pass.h @@ -238,8 +238,9 @@ class Printer : public Pass { std::ostream& o; public: - Printer() : o(std::cout) {} - Printer(std::ostream* o) : o(*o) {} + Printer(std::ostream* o = nullptr) : o(o ? *o : std::cout) { + name = "printer"; + } void run(PassRunner* runner, Module* module) override; }; diff --git a/src/passes/CoalesceLocals.cpp b/src/passes/CoalesceLocals.cpp index ce9b2d291c1..2a5bd5730e3 100644 --- a/src/passes/CoalesceLocals.cpp +++ b/src/passes/CoalesceLocals.cpp @@ -632,7 +632,7 @@ static void removeIfCopy(Expression** origin, SetLocal* set, If* iff, Expression if (!iff->ifTrue) { Builder(*module).flip(iff); } - iff->finalize(); + iff->finalize(none); } } diff --git a/src/passes/DeadCodeElimination.cpp b/src/passes/DeadCodeElimination.cpp index 1099cc6f87e..38627354b34 100644 --- a/src/passes/DeadCodeElimination.cpp +++ b/src/passes/DeadCodeElimination.cpp @@ -122,10 +122,11 @@ struct DeadCodeElimination : public WalkerPasstype) && 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. + if (isConcreteWasmType(block->type)) { + // the last element may be unreachable, and we may need to propagate our + // concrete type to it + block->finalize(block->type); + } } } } diff --git a/src/passes/MergeBlocks.cpp b/src/passes/MergeBlocks.cpp index 383837beb26..c3f030ff8c1 100644 --- a/src/passes/MergeBlocks.cpp +++ b/src/passes/MergeBlocks.cpp @@ -170,7 +170,7 @@ static void optimizeBlock(Block* curr, Module* module) { // reuse the drop drop->value = child->list.back(); child->list.back() = drop; - child->finalize(); + child->finalize(none); curr->list[i] = child; more = true; changed = true; @@ -226,7 +226,12 @@ struct MergeBlocks : public WalkerPasslist.back() = curr; - block->finalize(); // last block element was our input, and is now our output, which may differ TODO optimize + block->finalize(curr->type); // last block element was our input, and is now our output + if (block->type == unreachable && curr == getFunction()->body && getFunction()->result != none) { + // corner case: if the block isn't typed (e.g. ends in a return now), and is about to + // be placed as the function's fallthrough, then it must be typed if the function returns + block->finalize(getFunction()->result); + } replaceCurrent(block); return block; } else { diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp index 69a7c4ff117..a3c9a76ffd9 100644 --- a/src/passes/RemoveUnusedBrs.cpp +++ b/src/passes/RemoveUnusedBrs.cpp @@ -219,6 +219,7 @@ struct RemoveUnusedBrs : public WalkerPassifTrue)) { iff->ifFalse = builder.stealSlice(block, i + 1, list.size()); + block->finalize(block->type); return true; } } else { @@ -227,9 +228,11 @@ struct RemoveUnusedBrs : public WalkerPasstype)); // can't be, since in the middle of a block if (ExpressionAnalyzer::obviouslyDoesNotFlowOut(iff->ifTrue)) { iff->ifFalse = builder.blockifyMerge(iff->ifFalse, builder.stealSlice(block, i + 1, list.size())); + block->finalize(block->type); return true; } else if (ExpressionAnalyzer::obviouslyDoesNotFlowOut(iff->ifFalse)) { iff->ifTrue = builder.blockifyMerge(iff->ifTrue, builder.stealSlice(block, i + 1, list.size())); + block->finalize(block->type); return true; } } @@ -256,6 +259,7 @@ struct RemoveUnusedBrs : public WalkerPasscondition, builder.makeBreak(brIf->name), builder.stealSlice(block, i + 1, list.size())); + block->finalize(block->type); return true; } } diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp index 32b596eef1a..7abe91ca212 100644 --- a/src/passes/pass.cpp +++ b/src/passes/pass.cpp @@ -138,7 +138,9 @@ void PassRunner::addDefaultGlobalOptimizationPasses() { } void PassRunner::run() { - if (options.debug) { + static int passDebug = getenv("BINARYEN_PASS_DEBUG") ? atoi(getenv("BINARYEN_PASS_DEBUG")) : 0; + // debug the passes if relevant. be careful to not debug a single print pass, as that is how we print (we would infinitely recurse) + if ((options.debug || passDebug) && !(passes.size() == 1 && passes[0]->name == "printer")) { // for debug logging purposes, run each pass in full before running the other auto totalTime = std::chrono::duration(0); size_t padding = 0; @@ -146,11 +148,10 @@ void PassRunner::run() { for (auto pass : passes) { padding = std::max(padding, pass->name.size()); } - bool passDebug = getenv("BINARYEN_PASS_DEBUG") && getenv("BINARYEN_PASS_DEBUG")[0] != '0'; for (auto* pass : passes) { // ignoring the time, save a printout of the module before, in case this pass breaks it, so we can print the before and after std::stringstream moduleBefore; - if (passDebug) { + if (passDebug >= 2) { WasmPrinter::printModule(wasm, moduleBefore); } // prepare to run @@ -174,10 +175,10 @@ void PassRunner::run() { // validate, ignoring the time std::cerr << "[PassRunner] (validating)\n"; if (!WasmValidator().validate(*wasm, false, options.validateGlobally)) { - if (passDebug) { + 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=1 in the env to see the earlier state\n"; + std::cerr << "Last pass (" << pass->name << ") broke validation. Run with BINARYEN_PASS_DEBUG=2 in the env to see the earlier state\n"; } abort(); } diff --git a/src/s2wasm.h b/src/s2wasm.h index faa31ae922d..49e2566a532 100644 --- a/src/s2wasm.h +++ b/src/s2wasm.h @@ -1175,6 +1175,10 @@ class S2WasmBuilder { bstack.push_back(curr); } else if (match("end_block")) { auto* block = bstack.back()->cast(); + // wasm disallows an empty block with a type + if (isConcreteWasmType(block->type) && block->list.empty()) { + block->list.push_back(builder.makeUnreachable()); + } block->finalize(block->type); bstack.pop_back(); } else if (peek(".LBB")) { @@ -1283,7 +1287,7 @@ class S2WasmBuilder { bstack.pop_back(); // remove the base block for the function body assert(bstack.empty()); assert(estack.empty()); - func->body->dynCast()->finalize(); + func->body->dynCast()->finalize(func->result); wasm->addFunction(func); } diff --git a/src/wasm-validator.h b/src/wasm-validator.h index 28e575ca48d..d018f9cd519 100644 --- a/src/wasm-validator.h +++ b/src/wasm-validator.h @@ -41,6 +41,7 @@ #include "wasm.h" #include "wasm-printing.h" +#include "ast_utils.h" namespace wasm { @@ -127,8 +128,20 @@ struct WasmValidator : public PostWalker> } } } - if (!isConcreteWasmType(curr->type) && curr->list.size() > 0) { - shouldBeFalse(isConcreteWasmType(curr->list.back()->type), curr, "block with no value cannot have a last element with a value"); + if (curr->list.size() > 0) { + auto* last = curr->list.back(); + if (!isConcreteWasmType(curr->type)) { + shouldBeFalse(isConcreteWasmType(last->type), curr, "block with no value cannot have a last element with a value"); + } else { + // if we return, then control flow children must have that type, not even unreachable + // non-control flow lasts can be unreachable, but if they are not, must be equal + if (last->type != unreachable || ExpressionAnalyzer::isControlFlowStructure(last)) { + shouldBeEqual(last->type, curr->type, curr, "block fallthrough must have right type"); + } + } + } + if (isConcreteWasmType(curr->type)) { + shouldBeTrue(curr->list.size() > 0, curr, "a block with a type cannot be empty"); } } @@ -149,6 +162,10 @@ struct WasmValidator : public PostWalker> if (curr->type == none) { shouldBeFalse(isConcreteWasmType(curr->body->type), curr, "bad body for a loop that has no value"); } + // if we return, then control flow children must have that type, not even unreachable + if (isConcreteWasmType(curr->type)) { + if (ExpressionAnalyzer::isControlFlowStructure(curr->body)) shouldBeEqual(curr->body->type, curr->type, curr, "loop child must have right type"); + } } void visitIf(If *curr) { @@ -156,6 +173,11 @@ struct WasmValidator : public PostWalker> if (!curr->ifFalse) { shouldBeFalse(isConcreteWasmType(curr->ifTrue->type), curr, "if without else must not return a value in body"); } + // if we return, then control flow children must have that type, not even unreachable + if (isConcreteWasmType(curr->type)) { + if (ExpressionAnalyzer::isControlFlowStructure(curr->ifTrue)) shouldBeEqual(curr->ifTrue->type, curr->type, curr, "ifTrue child must have right type"); + if (ExpressionAnalyzer::isControlFlowStructure(curr->ifFalse)) shouldBeEqual(curr->ifFalse->type, curr->type, curr, "ifFalse child must have right type"); + } } // override scan to add a pre and a post check task to all nodes @@ -413,15 +435,17 @@ struct WasmValidator : public PostWalker> } void visitFunction(Function *curr) { - // if function has no result, it is ignored - // if body is unreachable, it might be e.g. a return - if (curr->body->type != unreachable) { - shouldBeEqual(curr->result, curr->body->type, curr->body, "function body type must match, if function returns"); - } - if (curr->result != none) { // TODO: over previous too? + if (curr->result != none) { + // if the body is concretely typed, it must be correct. + // if it is a control flow structure, then it must always be correct, even unreachable is bad + if (curr->body->type != unreachable || ExpressionAnalyzer::isControlFlowStructure(curr->body)) { + shouldBeEqual(curr->result, curr->body->type, curr->body, "function body type must match, if function returns"); + } if (returnType != unreachable) { shouldBeEqual(curr->result, returnType, curr->body, "function result must match, if function returns"); } + } else { + shouldBeTrue(!isConcreteWasmType(curr->body->type), curr->body, "if function does not return, body cannot be concretely typed"); } returnType = unreachable; labelNames.clear(); diff --git a/src/wasm.h b/src/wasm.h index 75d6a174c52..64da40a7bb7 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -959,23 +959,31 @@ class Nop : public SpecificExpression { class Block : public SpecificExpression { public: - Block(MixedArena& allocator) : list(allocator) {} + Block(MixedArena& allocator) : list(allocator) { + type = unreachable; + } Name name; ExpressionList list; // set the type given you know its type, which is the case when parsing // s-expression or binary, as explicit types are given. the only additional work - // this does is to set the type to unreachable in the cases that is needed. + // this does is to set the type to unreachable in the cases that is needed, and + // to propage a new concrete type to children as needed. void finalize(WasmType type_); - // set the type purely based on its contents. this scans the block, so it is not fast + // set the type purely based on its contents. this scans the block, so it is not fast. + // if the type is already something concrete, we do not alter it, so that if our + // contents to not imply a result but the parent does, then we remain valid + // we also propage a new concrete type to children as needed. void finalize(); }; class If : public SpecificExpression { public: - If() : ifFalse(nullptr) {} + If() : ifFalse(nullptr) { + type = unreachable; + } If(MixedArena& allocator) : If() {} Expression* condition; @@ -984,27 +992,37 @@ class If : public SpecificExpression { // set the type given you know its type, which is the case when parsing // s-expression or binary, as explicit types are given. the only additional work - // this does is to set the type to unreachable in the cases that is needed. + // this does is to set the type to unreachable in the cases that is needed, and + // to propage a new concrete type to children as needed. void finalize(WasmType type_); // set the type purely based on its contents. + // if the type is already something concrete, we do not alter it, so that if our + // contents to not imply a result but the parent does, then we remain valid + // we also propage a new concrete type to children as needed. void finalize(); }; class Loop : public SpecificExpression { public: - Loop() {} - Loop(MixedArena& allocator) {} + Loop() { + type = unreachable; + } + Loop(MixedArena& allocator) : Loop() {} Name name; Expression* body; // set the type given you know its type, which is the case when parsing // s-expression or binary, as explicit types are given. the only additional work - // this does is to set the type to unreachable in the cases that is needed. + // this does is to set the type to unreachable in the cases that is needed, and + // to propage a new concrete type to children as needed. void finalize(WasmType type_); // set the type purely based on its contents. + // if the type is already something concrete, we do not alter it, so that if our + // contents to not imply a result but the parent does, then we remain valid + // we also propage a new concrete type to children as needed. void finalize(); }; diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 3fcb85c6b9e..d8450cd8bd0 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -556,6 +556,13 @@ void SExpressionWasmBuilder::parseFunction(Element& s, bool preParseImport) { body = allocator.alloc(); } if (currFunction->result != result) throw ParseException("bad func declaration", s.line, s.col); + // if the function returns, the fallthrough must have the right type + if (currFunction->result != none && body->type != currFunction->result) { + auto* block = allocator.alloc(); + block->list.push_back(body); + block->finalize(currFunction->result); + body = block; + } currFunction->body = body; currFunction->type = type; wasm.addFunction(currFunction.release()); diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index cf58949de1b..72e45c51c33 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -133,6 +133,31 @@ static WasmType mergeTypes(std::vector& types) { return type; } +// when a block is changed none=>i32, then its last element may need to be changed as well, etc. +static void propagateConcreteTypeToChildren(Expression* start, WasmType type) { + std::vector work; + work.push_back(start); + while (work.size() > 0) { + auto* curr = work.back(); + work.pop_back(); + if (curr != start && curr->type != unreachable) continue; + if (auto* block = curr->dynCast()) { + block->type = type; + if (block->list.size() > 0) { + work.push_back(block->list.back()); + } + } else if (auto* loop = curr->dynCast()) { + loop->type = type; + work.push_back(loop->body); + } else if (auto* iff = curr->dynCast()) { + assert(iff->ifFalse); // must be an if-else + iff->type = type; + work.push_back(iff->ifTrue); + work.push_back(iff->ifFalse); + } + } +} + void Block::finalize(WasmType type_) { type = type_; if (type == none && list.size() > 0) { @@ -142,9 +167,14 @@ void Block::finalize(WasmType type_) { } } } + if (isConcreteWasmType(type)) { + propagateConcreteTypeToChildren(this, type); + } } void Block::finalize() { + // if already set to a concrete value, keep it + if (isConcreteWasmType(type)) return; if (!name.is()) { // nothing branches here, so this is easy if (list.size() > 0) { @@ -157,6 +187,10 @@ void Block::finalize() { TypeSeeker seeker(this, this->name); type = mergeTypes(seeker.types); + + if (isConcreteWasmType(type)) { + propagateConcreteTypeToChildren(this, type); + } } void If::finalize(WasmType type_) { @@ -164,9 +198,14 @@ void If::finalize(WasmType type_) { if (type == none && (condition->type == unreachable || (ifTrue->type == unreachable && (!ifFalse || ifFalse->type == unreachable)))) { type = unreachable; } + if (isConcreteWasmType(type)) { + propagateConcreteTypeToChildren(this, type); + } } void If::finalize() { + // if already set to a concrete value, keep it + if (isConcreteWasmType(type)) return; if (condition->type == unreachable) { type = unreachable; } else if (ifFalse) { @@ -174,8 +213,10 @@ void If::finalize() { type = ifTrue->type; } else if (isConcreteWasmType(ifTrue->type) && ifFalse->type == unreachable) { type = ifTrue->type; + propagateConcreteTypeToChildren(this, type); } else if (isConcreteWasmType(ifFalse->type) && ifTrue->type == unreachable) { type = ifFalse->type; + propagateConcreteTypeToChildren(this, type); } else { type = none; } @@ -189,10 +230,19 @@ void Loop::finalize(WasmType type_) { if (type == none && body->type == unreachable) { type = unreachable; } + if (isConcreteWasmType(type)) { + propagateConcreteTypeToChildren(this, type); + } } void Loop::finalize() { + // if already set to a concrete value, keep it + if (isConcreteWasmType(type)) return; type = body->type; + + if (isConcreteWasmType(type)) { + propagateConcreteTypeToChildren(this, type); + } } } // namespace wasm diff --git a/test/dot_s/unreachable_blocks.wast b/test/dot_s/unreachable_blocks.wast index 3c7b0d158eb..8e3e3a7388a 100644 --- a/test/dot_s/unreachable_blocks.wast +++ b/test/dot_s/unreachable_blocks.wast @@ -15,6 +15,7 @@ (i32.const 2) ) (block $label$0 i32 + (unreachable) ) ) (func $unreachable_block_i64 (result i64) @@ -22,6 +23,7 @@ (i64.const 3) ) (block $label$0 i64 + (unreachable) ) ) (func $unreachable_block_f32 (result f32) @@ -29,6 +31,7 @@ (f32.const 4.5) ) (block $label$0 f32 + (unreachable) ) ) (func $unreachable_block_f64 (result f64) @@ -36,6 +39,7 @@ (f64.const 5.5) ) (block $label$0 f64 + (unreachable) ) ) (func $unreachable_loop_void diff --git a/test/emcc_hello_world.fromasm.imprecise.no-opts b/test/emcc_hello_world.fromasm.imprecise.no-opts index b4dcbcaa3a6..637233bfafd 100644 --- a/test/emcc_hello_world.fromasm.imprecise.no-opts +++ b/test/emcc_hello_world.fromasm.imprecise.no-opts @@ -30271,11 +30271,11 @@ ) (return (block i32 - (block - (set_global $tempRet0 - (get_local $h) - ) - (drop + (drop + (block i32 + (set_global $tempRet0 + (get_local $h) + ) (get_global $tempRet0) ) ) @@ -30306,11 +30306,11 @@ ) (return (block i32 - (block - (set_global $tempRet0 - (get_local $h) - ) - (drop + (drop + (block i32 + (set_global $tempRet0 + (get_local $h) + ) (get_global $tempRet0) ) ) @@ -30882,32 +30882,32 @@ ) (return (block i32 - (block - (set_global $tempRet0 - (i32.add + (drop + (block i32 + (set_global $tempRet0 (i32.add - (i32.shr_u - (get_local $$8) - (i32.const 16) - ) - (i32.mul - (get_local $$11) - (get_local $$6) - ) - ) - (i32.shr_u (i32.add - (i32.and + (i32.shr_u (get_local $$8) - (i32.const 65535) + (i32.const 16) + ) + (i32.mul + (get_local $$11) + (get_local $$6) ) - (get_local $$12) ) - (i32.const 16) + (i32.shr_u + (i32.add + (i32.and + (get_local $$8) + (i32.const 65535) + ) + (get_local $$12) + ) + (i32.const 16) + ) ) ) - ) - (drop (get_global $tempRet0) ) ) @@ -31285,11 +31285,11 @@ ) (return (block i32 - (block - (set_global $tempRet0 - (get_local $$10$1) - ) - (drop + (drop + (block i32 + (set_global $tempRet0 + (get_local $$10$1) + ) (get_global $tempRet0) ) ) @@ -31326,26 +31326,26 @@ ) (return (block i32 - (block - (set_global $tempRet0 - (i32.or - (i32.add + (drop + (block i32 + (set_global $tempRet0 + (i32.or (i32.add - (i32.mul - (get_local $$b$1) - (get_local $$x_sroa_0_0_extract_trunc) + (i32.add + (i32.mul + (get_local $$b$1) + (get_local $$x_sroa_0_0_extract_trunc) + ) + (get_local $$2) ) - (get_local $$2) + (get_local $$1$1) + ) + (i32.and + (get_local $$1$1) + (i32.const 0) ) - (get_local $$1$1) - ) - (i32.and - (get_local $$1$1) - (i32.const 0) ) ) - ) - (drop (get_global $tempRet0) ) ) @@ -31403,16 +31403,16 @@ ) (return (block i32 - (block - (set_global $tempRet0 - (i32.load - (i32.add - (get_local $$rem) - (i32.const 4) + (drop + (block i32 + (set_global $tempRet0 + (i32.load + (i32.add + (get_local $$rem) + (i32.const 4) + ) ) ) - ) - (drop (get_global $tempRet0) ) ) diff --git a/test/emcc_hello_world.fromasm.no-opts b/test/emcc_hello_world.fromasm.no-opts index 10cae59e8f6..2c3a2b99d6a 100644 --- a/test/emcc_hello_world.fromasm.no-opts +++ b/test/emcc_hello_world.fromasm.no-opts @@ -30277,11 +30277,11 @@ ) (return (block i32 - (block - (set_global $tempRet0 - (get_local $h) - ) - (drop + (drop + (block i32 + (set_global $tempRet0 + (get_local $h) + ) (get_global $tempRet0) ) ) @@ -30312,11 +30312,11 @@ ) (return (block i32 - (block - (set_global $tempRet0 - (get_local $h) - ) - (drop + (drop + (block i32 + (set_global $tempRet0 + (get_local $h) + ) (get_global $tempRet0) ) ) @@ -30888,32 +30888,32 @@ ) (return (block i32 - (block - (set_global $tempRet0 - (i32.add + (drop + (block i32 + (set_global $tempRet0 (i32.add - (i32.shr_u - (get_local $$8) - (i32.const 16) - ) - (i32.mul - (get_local $$11) - (get_local $$6) - ) - ) - (i32.shr_u (i32.add - (i32.and + (i32.shr_u (get_local $$8) - (i32.const 65535) + (i32.const 16) + ) + (i32.mul + (get_local $$11) + (get_local $$6) ) - (get_local $$12) ) - (i32.const 16) + (i32.shr_u + (i32.add + (i32.and + (get_local $$8) + (i32.const 65535) + ) + (get_local $$12) + ) + (i32.const 16) + ) ) ) - ) - (drop (get_global $tempRet0) ) ) @@ -31291,11 +31291,11 @@ ) (return (block i32 - (block - (set_global $tempRet0 - (get_local $$10$1) - ) - (drop + (drop + (block i32 + (set_global $tempRet0 + (get_local $$10$1) + ) (get_global $tempRet0) ) ) @@ -31332,26 +31332,26 @@ ) (return (block i32 - (block - (set_global $tempRet0 - (i32.or - (i32.add + (drop + (block i32 + (set_global $tempRet0 + (i32.or (i32.add - (i32.mul - (get_local $$b$1) - (get_local $$x_sroa_0_0_extract_trunc) + (i32.add + (i32.mul + (get_local $$b$1) + (get_local $$x_sroa_0_0_extract_trunc) + ) + (get_local $$2) ) - (get_local $$2) + (get_local $$1$1) + ) + (i32.and + (get_local $$1$1) + (i32.const 0) ) - (get_local $$1$1) - ) - (i32.and - (get_local $$1$1) - (i32.const 0) ) ) - ) - (drop (get_global $tempRet0) ) ) @@ -31409,16 +31409,16 @@ ) (return (block i32 - (block - (set_global $tempRet0 - (i32.load - (i32.add - (get_local $$rem) - (i32.const 4) + (drop + (block i32 + (set_global $tempRet0 + (i32.load + (i32.add + (get_local $$rem) + (i32.const 4) + ) ) ) - ) - (drop (get_global $tempRet0) ) ) diff --git a/test/example/c-api-kitchen-sink.c b/test/example/c-api-kitchen-sink.c index 77e7f0b05bf..1ede638ec46 100644 --- a/test/example/c-api-kitchen-sink.c +++ b/test/example/c-api-kitchen-sink.c @@ -182,7 +182,7 @@ void test_core() { makeBinary(module, BinaryenGtFloat64(), 4), makeBinary(module, BinaryenGeFloat32(), 3), // All the rest - BinaryenBlock(module, NULL, NULL, 0), // block with no name + BinaryenBlock(module, NULL, NULL, 0, BinaryenNone()), // block with no name BinaryenIf(module, temp1, temp2, temp3), BinaryenIf(module, temp4, temp5, NULL), BinaryenLoop(module, "in", makeInt32(module, 0)), @@ -224,11 +224,11 @@ void test_core() { BinaryenExpressionPrint(valueList[3]); // test printing a standalone expression // Make the main body of the function. and one block with a return value, one without - BinaryenExpressionRef value = BinaryenBlock(module, "the-value", valueList, sizeof(valueList) / sizeof(BinaryenExpressionRef)); + BinaryenExpressionRef value = BinaryenBlock(module, "the-value", valueList, sizeof(valueList) / sizeof(BinaryenExpressionRef), BinaryenInt32()); BinaryenExpressionRef droppedValue = BinaryenDrop(module, value); - BinaryenExpressionRef nothing = BinaryenBlock(module, "the-nothing", &droppedValue, 1); + BinaryenExpressionRef nothing = BinaryenBlock(module, "the-nothing", &droppedValue, 1, BinaryenNone()); BinaryenExpressionRef bodyList[] = { nothing, makeInt32(module, 42) }; - BinaryenExpressionRef body = BinaryenBlock(module, "the-body", bodyList, 2); + BinaryenExpressionRef body = BinaryenBlock(module, "the-body", bodyList, 2, BinaryenInt32()); // Create the function BinaryenType localTypes[] = { BinaryenInt32() }; @@ -456,8 +456,9 @@ void test_relooper() { { // return in a block RelooperRef relooper = RelooperCreate(); + BinaryenExpressionRef listList[] = { makeCallCheck(module, 42), BinaryenReturn(module, makeInt32(module, 1337)) }; - BinaryenExpressionRef list = BinaryenBlock(module, "the-list", listList, 2); + BinaryenExpressionRef list = BinaryenBlock(module, "the-list", listList, 2, BinaryenInt32()); RelooperBlockRef block = RelooperAddBlock(relooper, list); BinaryenExpressionRef body = RelooperRenderAndDispose(relooper, block, 0, module); BinaryenFunctionRef sinker = BinaryenAddFunction(module, "return", i, localTypes, 1, body); diff --git a/test/example/c-api-kitchen-sink.txt b/test/example/c-api-kitchen-sink.txt index 4eee5c0c01a..05011f354e7 100644 --- a/test/example/c-api-kitchen-sink.txt +++ b/test/example/c-api-kitchen-sink.txt @@ -399,12 +399,10 @@ BinaryenFloat64: 4 ) (block ) - (if - (i32.const 1) - (drop + (drop + (if i32 + (i32.const 1) (i32.const 2) - ) - (drop (i32.const 3) ) ) @@ -1013,7 +1011,7 @@ raw: ) (func $return (type $i) (result i32) (local $0 i32) - (block $the-list + (block $the-list i32 (call $check (i32.const 42) ) @@ -1280,7 +1278,7 @@ int main() { expressions[196] = BinaryenBinary(the_module, 62, expressions[195], expressions[194]); { BinaryenExpressionRef children[] = { 0 }; - expressions[197] = BinaryenBlock(the_module, NULL, children, 0); + expressions[197] = BinaryenBlock(the_module, NULL, children, 0, 0); } expressions[198] = BinaryenIf(the_module, expressions[13], expressions[14], expressions[15]); expressions[199] = BinaryenIf(the_module, expressions[16], expressions[17], expressions[0]); @@ -1348,17 +1346,17 @@ int main() { ) { BinaryenExpressionRef children[] = { expressions[30], expressions[32], expressions[34], expressions[36], expressions[38], expressions[40], expressions[42], expressions[44], expressions[46], expressions[48], expressions[50], expressions[52], expressions[54], expressions[56], expressions[58], expressions[60], expressions[62], expressions[64], expressions[66], expressions[68], expressions[70], expressions[72], expressions[74], expressions[76], expressions[78], expressions[80], expressions[82], expressions[84], expressions[86], expressions[88], expressions[90], expressions[92], expressions[94], expressions[96], expressions[98], expressions[100], expressions[103], expressions[106], expressions[109], expressions[112], expressions[115], expressions[118], expressions[121], expressions[124], expressions[127], expressions[130], expressions[133], expressions[136], expressions[139], expressions[142], expressions[145], expressions[148], expressions[151], expressions[154], expressions[157], expressions[160], expressions[163], expressions[166], expressions[169], expressions[172], expressions[175], expressions[178], expressions[181], expressions[184], expressions[187], expressions[190], expressions[193], expressions[196], expressions[197], expressions[198], expressions[199], expressions[201], expressions[203], expressions[204], expressions[206], expressions[208], expressions[209], expressions[210], expressions[212], expressions[214], expressions[217], expressions[220], expressions[222], expressions[224], expressions[227], expressions[229], expressions[231], expressions[233], expressions[235], expressions[236], expressions[237], expressions[238], expressions[240], expressions[241], expressions[242] }; - expressions[243] = BinaryenBlock(the_module, "the-value", children, 95); + expressions[243] = BinaryenBlock(the_module, "the-value", children, 95, 1); } expressions[244] = BinaryenDrop(the_module, expressions[243]); { BinaryenExpressionRef children[] = { expressions[244] }; - expressions[245] = BinaryenBlock(the_module, "the-nothing", children, 1); + expressions[245] = BinaryenBlock(the_module, "the-nothing", children, 1, 0); } expressions[246] = BinaryenConst(the_module, BinaryenLiteralInt32(42)); { BinaryenExpressionRef children[] = { expressions[245], expressions[246] }; - expressions[247] = BinaryenBlock(the_module, "the-body", children, 2); + expressions[247] = BinaryenBlock(the_module, "the-body", children, 2, 1); } { BinaryenType varTypes[] = { 1 }; @@ -1792,12 +1790,10 @@ int main() { ) (block ) - (if - (i32.const 1) - (drop + (drop + (if i32 + (i32.const 1) (i32.const 2) - ) - (drop (i32.const 3) ) ) @@ -2380,7 +2376,7 @@ int main() { expressions[139] = BinaryenReturn(the_module, expressions[138]); { BinaryenExpressionRef children[] = { expressions[137], expressions[139] }; - expressions[140] = BinaryenBlock(the_module, "the-list", children, 2); + expressions[140] = BinaryenBlock(the_module, "the-list", children, 2, 1); } relooperBlocks[0] = RelooperAddBlock(the_relooper, expressions[140]); expressions[141] = RelooperRenderAndDispose(the_relooper, relooperBlocks[0], 0, the_module); @@ -2866,7 +2862,7 @@ raw: ) (func $return (type $i) (result i32) (local $0 i32) - (block $the-list + (block $the-list i32 (call $check (i32.const 42) ) diff --git a/test/example/c-api-kitchen-sink.txt.txt b/test/example/c-api-kitchen-sink.txt.txt index cbccff23264..a624a71fa26 100644 --- a/test/example/c-api-kitchen-sink.txt.txt +++ b/test/example/c-api-kitchen-sink.txt.txt @@ -394,12 +394,10 @@ ) (block ) - (if - (i32.const 1) - (drop + (drop + (if i32 + (i32.const 1) (i32.const 2) - ) - (drop (i32.const 3) ) ) @@ -1007,7 +1005,7 @@ ) (func $return (type $i) (result i32) (local $0 i32) - (block $the-list + (block $the-list i32 (call $check (i32.const 42) ) diff --git a/test/example/c-api-unused-mem.cpp b/test/example/c-api-unused-mem.cpp index 69bee351ffb..2890ad2f2ab 100644 --- a/test/example/c-api-unused-mem.cpp +++ b/test/example/c-api-unused-mem.cpp @@ -22,7 +22,7 @@ int main() { the_relooper = RelooperCreate(); { BinaryenExpressionRef children[] = { 0 }; - expressions[1] = BinaryenBlock(the_module, "bb0", children, 0); + expressions[1] = BinaryenBlock(the_module, "bb0", children, 0, BinaryenNone()); } relooperBlocks[0] = RelooperAddBlock(the_relooper, expressions[1]); expressions[2] = BinaryenGetLocal(the_module, 0, 1); @@ -31,7 +31,7 @@ int main() { expressions[5] = BinaryenReturn(the_module, expressions[0]); { BinaryenExpressionRef children[] = { expressions[4], expressions[5] }; - expressions[6] = BinaryenBlock(the_module, "bb1", children, 2); + expressions[6] = BinaryenBlock(the_module, "bb1", children, 2, BinaryenNone()); } relooperBlocks[1] = RelooperAddBlock(the_relooper, expressions[6]); RelooperAddBranch(relooperBlocks[0], relooperBlocks[1], expressions[0], expressions[0]); @@ -69,7 +69,7 @@ int main() { } { BinaryenExpressionRef children[] = { expressions[13], expressions[14] }; - expressions[15] = BinaryenBlock(the_module, NULL, children, 2); + expressions[15] = BinaryenBlock(the_module, NULL, children, 2, BinaryenNone()); } BinaryenAddExport(the_module, "__wasm_start", "rust_entry"); { diff --git a/test/example/relooper-fuzz.c b/test/example/relooper-fuzz.c index 9fd11096fc9..83552b6b965 100644 --- a/test/example/relooper-fuzz.c +++ b/test/example/relooper-fuzz.c @@ -56,7 +56,7 @@ int main() { BinaryenLoad(module, 4, 0, 0, 0, BinaryenInt32(), BinaryenConst(module, BinaryenLiteralInt32(4))) ); BinaryenExpressionRef checkBodyList[] = { halter, incer, debugger, returner }; - BinaryenExpressionRef checkBody = BinaryenBlock(module, NULL, checkBodyList, sizeof(checkBodyList) / sizeof(BinaryenExpressionRef)); + BinaryenExpressionRef checkBody = BinaryenBlock(module, NULL, checkBodyList, sizeof(checkBodyList) / sizeof(BinaryenExpressionRef), BinaryenInt32()); BinaryenFunctionTypeRef i = BinaryenAddFunctionType(module, "i", BinaryenInt32(), NULL, 0); BinaryenAddFunction(module, "check", i, NULL, 0, checkBody); @@ -72,7 +72,7 @@ int main() { BinaryenCallImport(module, "print", args, 1, BinaryenNone()), BinaryenSetLocal(module, 0, BinaryenCall(module, "check", NULL, 0, BinaryenInt32())) }; - b0 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + b0 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2, BinaryenNone())); } RelooperBlockRef b1; @@ -82,7 +82,7 @@ int main() { BinaryenCallImport(module, "print", args, 1, BinaryenNone()), BinaryenSetLocal(module, 0, BinaryenCall(module, "check", NULL, 0, BinaryenInt32())) }; - b1 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + b1 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2, BinaryenNone())); } RelooperBlockRef b2; @@ -92,7 +92,7 @@ int main() { BinaryenCallImport(module, "print", args, 1, BinaryenNone()), BinaryenSetLocal(module, 0, BinaryenCall(module, "check", NULL, 0, BinaryenInt32())) }; - b2 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + b2 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2, BinaryenNone())); } RelooperBlockRef b3; @@ -102,7 +102,7 @@ int main() { BinaryenCallImport(module, "print", args, 1, BinaryenNone()), BinaryenSetLocal(module, 0, BinaryenCall(module, "check", NULL, 0, BinaryenInt32())) }; - b3 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + b3 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2, BinaryenNone())); } RelooperBlockRef b4; @@ -112,7 +112,7 @@ int main() { BinaryenCallImport(module, "print", args, 1, BinaryenNone()), BinaryenSetLocal(module, 0, BinaryenCall(module, "check", NULL, 0, BinaryenInt32())) }; - b4 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + b4 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2, BinaryenNone())); } RelooperBlockRef b5; @@ -122,7 +122,7 @@ int main() { BinaryenCallImport(module, "print", args, 1, BinaryenNone()), BinaryenSetLocal(module, 0, BinaryenCall(module, "check", NULL, 0, BinaryenInt32())) }; - b5 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + b5 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2, BinaryenNone())); } RelooperBlockRef b6; @@ -132,7 +132,7 @@ int main() { BinaryenCallImport(module, "print", args, 1, BinaryenNone()), BinaryenSetLocal(module, 0, BinaryenCall(module, "check", NULL, 0, BinaryenInt32())) }; - b6 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + b6 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2, BinaryenNone())); } RelooperBlockRef b7; @@ -142,7 +142,7 @@ int main() { BinaryenCallImport(module, "print", args, 1, BinaryenNone()), BinaryenSetLocal(module, 0, BinaryenCall(module, "check", NULL, 0, BinaryenInt32())) }; - b7 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + b7 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2, BinaryenNone())); } RelooperBlockRef b8; @@ -152,7 +152,7 @@ int main() { BinaryenCallImport(module, "print", args, 1, BinaryenNone()), BinaryenSetLocal(module, 0, BinaryenCall(module, "check", NULL, 0, BinaryenInt32())) }; - b8 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + b8 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2, BinaryenNone())); } RelooperAddBranch(b0, b5, BinaryenBinary(module, @@ -242,7 +242,7 @@ int main() { } } full[numDecisions] = body; - BinaryenExpressionRef all = BinaryenBlock(module, NULL, full, numDecisions + 1); + BinaryenExpressionRef all = BinaryenBlock(module, NULL, full, numDecisions + 1, BinaryenNone()); BinaryenFunctionTypeRef v = BinaryenAddFunctionType(module, "v", BinaryenNone(), NULL, 0); BinaryenType localTypes[] = { BinaryenInt32(), BinaryenInt32() }; // state, free-for-label diff --git a/test/example/relooper-fuzz1.c b/test/example/relooper-fuzz1.c index cca61e4f451..aebfbdb6135 100644 --- a/test/example/relooper-fuzz1.c +++ b/test/example/relooper-fuzz1.c @@ -64,7 +64,7 @@ int main() { BinaryenExpressionRef checkBodyList[] = { halter, incer, debugger, returner }; BinaryenExpressionRef checkBody = BinaryenBlock(module, - NULL, checkBodyList, sizeof(checkBodyList) / sizeof(BinaryenExpressionRef) + NULL, checkBodyList, sizeof(checkBodyList) / sizeof(BinaryenExpressionRef), BinaryenInt32() ); BinaryenFunctionTypeRef i = BinaryenAddFunctionType(module, "i", BinaryenInt32(), @@ -87,7 +87,7 @@ int main() { BinaryenInt32())) }; - b0 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + b0 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2, BinaryenNone())); } @@ -102,7 +102,7 @@ int main() { BinaryenInt32())) }; - b1 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + b1 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2, BinaryenNone())); } @@ -117,7 +117,7 @@ int main() { BinaryenInt32())) }; - b2 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + b2 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2, BinaryenNone())); } @@ -132,7 +132,7 @@ int main() { BinaryenInt32())) }; - b3 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + b3 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2, BinaryenNone())); } @@ -147,7 +147,7 @@ int main() { BinaryenInt32())) }; - b4 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + b4 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2, BinaryenNone())); } @@ -162,7 +162,7 @@ int main() { BinaryenInt32())) }; - b5 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + b5 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2, BinaryenNone())); } @@ -177,7 +177,7 @@ int main() { BinaryenInt32())) }; - b6 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + b6 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2, BinaryenNone())); } @@ -192,7 +192,7 @@ int main() { BinaryenInt32())) }; - b7 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + b7 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2, BinaryenNone())); } @@ -207,7 +207,7 @@ int main() { BinaryenInt32())) }; - b8 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + b8 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2, BinaryenNone())); } @@ -222,7 +222,7 @@ int main() { BinaryenInt32())) }; - b9 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + b9 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2, BinaryenNone())); } @@ -296,7 +296,7 @@ int main() { } full[numDecisions] = body; BinaryenExpressionRef all = BinaryenBlock(module, NULL, full, - numDecisions + 1); + numDecisions + 1, BinaryenNone()); BinaryenFunctionTypeRef v = BinaryenAddFunctionType(module, "v", BinaryenNone(), diff --git a/test/passes/inlining.txt b/test/passes/inlining.txt index 51d7c234770..68440ef7f73 100644 --- a/test/passes/inlining.txt +++ b/test/passes/inlining.txt @@ -57,8 +57,10 @@ ) (drop (block $__inlined_func$return i32 - (br $__inlined_func$return - (i32.const 5) + (block i32 + (br $__inlined_func$return + (i32.const 5) + ) ) ) ) diff --git a/test/passes/remove-unused-brs.txt b/test/passes/remove-unused-brs.txt index b95ace90d6c..edfca2a5dee 100644 --- a/test/passes/remove-unused-brs.txt +++ b/test/passes/remove-unused-brs.txt @@ -937,4 +937,22 @@ ) ) ) + (func $loop-if (type $2) (result i32) + (block $outer i32 + (loop $typed i32 + (if i32 + (i32.const 2) + (block $block i32 + (drop + (call $loop-if) + ) + (br $outer + (i32.const 0) + ) + ) + (br $typed) + ) + ) + ) + ) ) diff --git a/test/passes/remove-unused-brs.wast b/test/passes/remove-unused-brs.wast index 8b0ad357bed..4bd100b904c 100644 --- a/test/passes/remove-unused-brs.wast +++ b/test/passes/remove-unused-brs.wast @@ -831,5 +831,21 @@ ) ) ) + (func $loop-if (result i32) + (block $outer i32 + (loop $typed i32 + ;; we can move the br after us into our if-else, but that then means we are the final + ;; element in the block, and so must be typed + (if + (i32.const 2) + (block + (drop (call $loop-if)) + (br $outer (i32.const 0)) + ) + ) + (br $typed) + ) + ) + ) ) diff --git a/test/passes/remove-unused-names_vacuum.txt b/test/passes/remove-unused-names_vacuum.txt index 4da159f92c2..b1fd9ad2cc5 100644 --- a/test/passes/remove-unused-names_vacuum.txt +++ b/test/passes/remove-unused-names_vacuum.txt @@ -2,7 +2,14 @@ (type $0 (func (result i32))) (memory $0 0) (func $return-i32-but-body-is-unreachable3 (type $0) (result i32) + (local $label i32) + (loop $while-in$1 i32 + (br $while-in$1) + ) + ) + (func $return-i32-but-body-is-unreachable4 (type $0) (result i32) (local $label i32) (unreachable) + (i32.const 0) ) ) diff --git a/test/passes/remove-unused-names_vacuum.wast b/test/passes/remove-unused-names_vacuum.wast index 04b08c332e8..8b73960d820 100644 --- a/test/passes/remove-unused-names_vacuum.wast +++ b/test/passes/remove-unused-names_vacuum.wast @@ -1,11 +1,20 @@ (module (func $return-i32-but-body-is-unreachable3 (result i32) + (local $label i32) + (block i32 ;; without a name here, vaccum had a too-eager bug + (loop $while-in$1 i32 + (br $while-in$1) + ) + ) + ) + (func $return-i32-but-body-is-unreachable4 (result i32) (local $label i32) (block ;; without a name here, vaccum had a too-eager bug (loop $while-in$1 (br $while-in$1) ) ) + (i32.const 0) ) ) diff --git a/test/passes/vacuum_remove-unused-names_merge-blocks.txt b/test/passes/vacuum_remove-unused-names_merge-blocks.txt new file mode 100644 index 00000000000..a351c443b4a --- /dev/null +++ b/test/passes/vacuum_remove-unused-names_merge-blocks.txt @@ -0,0 +1,12 @@ +(module + (type $0 (func (param i32) (result i32))) + (memory $0 0) + (func $return-block (type $0) (param $x i32) (result i32) + (set_local $x + (get_local $x) + ) + (return + (get_local $x) + ) + ) +) diff --git a/test/passes/vacuum_remove-unused-names_merge-blocks.wast b/test/passes/vacuum_remove-unused-names_merge-blocks.wast new file mode 100644 index 00000000000..0793091538d --- /dev/null +++ b/test/passes/vacuum_remove-unused-names_merge-blocks.wast @@ -0,0 +1,16 @@ +(module + ;; vacuum and remove-unused names leave us with a return at the top, and then + ;; merge-blocks wants to move the first line of the block into an outer block + ;; that then becomes the fallthrough of the function, so it must be properly typed! + ;; and here the new last element is a return, with unreachable type, bad for a block + ;; in that position + (func $return-block (param $x i32) (result i32) + (return + (block i32 + (set_local $x (get_local $x)) + (get_local $x) + ) + ) + ) +) + diff --git a/test/two_sides.fromasm.imprecise.no-opts b/test/two_sides.fromasm.imprecise.no-opts index 94bca724540..61789b79205 100644 --- a/test/two_sides.fromasm.imprecise.no-opts +++ b/test/two_sides.fromasm.imprecise.no-opts @@ -6,11 +6,11 @@ (export "_test" (func $_test)) (func $_test (param $i1 i32) (param $i2 i32) (param $i3 i32) (param $i4 i32) (param $i5 i32) (result i32) (local $d6 f64) - (if + (if i32 (i32.eqz (get_local $i5) ) - (block + (block i32 (set_local $d6 (f64.convert_s/i32 (i32.mul @@ -44,7 +44,7 @@ (get_local $i5) ) ) - (block + (block i32 (set_local $d6 (f64.convert_s/i32 (i32.mul diff --git a/test/two_sides.fromasm.no-opts b/test/two_sides.fromasm.no-opts index 0976e4c906b..d1bd24b27db 100644 --- a/test/two_sides.fromasm.no-opts +++ b/test/two_sides.fromasm.no-opts @@ -8,11 +8,11 @@ (export "_test" (func $_test)) (func $_test (param $i1 i32) (param $i2 i32) (param $i3 i32) (param $i4 i32) (param $i5 i32) (result i32) (local $d6 f64) - (if + (if i32 (i32.eqz (get_local $i5) ) - (block + (block i32 (set_local $d6 (f64.convert_s/i32 (i32.mul @@ -46,7 +46,7 @@ (get_local $i5) ) ) - (block + (block i32 (set_local $d6 (f64.convert_s/i32 (i32.mul diff --git a/test/unit.fromasm b/test/unit.fromasm index 4f7b2c6a99f..76e383ac839 100644 --- a/test/unit.fromasm +++ b/test/unit.fromasm @@ -354,30 +354,32 @@ ) (func $aborts (drop - (call $abort - (f64.const 0) - ) - ) - (drop - (call $abort - (f64.convert_s/i32 - (i32.const 55) + (block f64 + (drop + (call $abort + (f64.const 0) + ) + ) + (drop + (call $abort + (f64.convert_s/i32 + (i32.const 55) + ) + ) + ) + (drop + (call $abort + (f64.const 0) + ) + ) + (drop + (call $abort + (f64.const 12.34) + ) + ) + (call $abort + (f64.const 56.779998779296875) ) - ) - ) - (drop - (call $abort - (f64.const 0) - ) - ) - (drop - (call $abort - (f64.const 12.34) - ) - ) - (drop - (call $abort - (f64.const 56.779998779296875) ) ) ) diff --git a/test/unit.fromasm.imprecise b/test/unit.fromasm.imprecise index caa8e4f3355..82718805392 100644 --- a/test/unit.fromasm.imprecise +++ b/test/unit.fromasm.imprecise @@ -330,30 +330,32 @@ ) (func $aborts (drop - (call $abort - (f64.const 0) - ) - ) - (drop - (call $abort - (f64.convert_s/i32 - (i32.const 55) + (block f64 + (drop + (call $abort + (f64.const 0) + ) + ) + (drop + (call $abort + (f64.convert_s/i32 + (i32.const 55) + ) + ) + ) + (drop + (call $abort + (f64.const 0) + ) + ) + (drop + (call $abort + (f64.const 12.34) + ) + ) + (call $abort + (f64.const 56.779998779296875) ) - ) - ) - (drop - (call $abort - (f64.const 0) - ) - ) - (drop - (call $abort - (f64.const 12.34) - ) - ) - (drop - (call $abort - (f64.const 56.779998779296875) ) ) ) diff --git a/test/unit.fromasm.imprecise.no-opts b/test/unit.fromasm.imprecise.no-opts index eafbb32f707..3bc6482b78d 100644 --- a/test/unit.fromasm.imprecise.no-opts +++ b/test/unit.fromasm.imprecise.no-opts @@ -614,31 +614,33 @@ ) (func $aborts (drop - (call $abort - (f64.const 0) - ) - ) - (drop - (call $abort - (f64.convert_s/i32 - (i32.const 55) + (block f64 + (drop + (call $abort + (f64.const 0) + ) ) - ) - ) - (drop - (call $abort - (f64.const 0) - ) - ) - (drop - (call $abort - (f64.const 12.34) - ) - ) - (drop - (call $abort - (f64.promote/f32 - (f32.const 56.779998779296875) + (drop + (call $abort + (f64.convert_s/i32 + (i32.const 55) + ) + ) + ) + (drop + (call $abort + (f64.const 0) + ) + ) + (drop + (call $abort + (f64.const 12.34) + ) + ) + (call $abort + (f64.promote/f32 + (f32.const 56.779998779296875) + ) ) ) ) @@ -714,16 +716,16 @@ ) ) (block i32 - (block - (block - (drop - (i32.const 4) - ) + (drop + (block i32 (drop - (i32.const 5) + (block i32 + (drop + (i32.const 4) + ) + (i32.const 5) + ) ) - ) - (drop (i32.const 6) ) ) @@ -778,20 +780,20 @@ ) ) (block i32 - (block - (block - (drop - (call $lb - (i32.const 4) - ) - ) + (drop + (block i32 (drop - (call $lb - (i32.const 5) + (block i32 + (drop + (call $lb + (i32.const 4) + ) + ) + (call $lb + (i32.const 5) + ) ) ) - ) - (drop (call $lb (i32.const 6) ) @@ -929,7 +931,6 @@ ) (br $do-once) ) - (nop) ) ) (func $dropCall (result i32) @@ -980,11 +981,11 @@ (func $usesSetGlobal2 (result i32) (return (block i32 - (block - (set_global $Int - (i32.const 40) - ) - (drop + (drop + (block i32 + (set_global $Int + (i32.const 40) + ) (get_global $Int) ) ) @@ -1014,7 +1015,6 @@ (i32.const 1337) ) ) - (nop) ) ) (func $ifChainEmpty (param $label i32) (result i32) @@ -1776,7 +1776,6 @@ ) (br $do-once) ) - (nop) ) (return) ) @@ -1800,7 +1799,6 @@ ) ) ) - (nop) ) (return) ) diff --git a/test/unit.fromasm.no-opts b/test/unit.fromasm.no-opts index 29fede80c71..437301587b1 100644 --- a/test/unit.fromasm.no-opts +++ b/test/unit.fromasm.no-opts @@ -620,31 +620,33 @@ ) (func $aborts (drop - (call $abort - (f64.const 0) - ) - ) - (drop - (call $abort - (f64.convert_s/i32 - (i32.const 55) + (block f64 + (drop + (call $abort + (f64.const 0) + ) ) - ) - ) - (drop - (call $abort - (f64.const 0) - ) - ) - (drop - (call $abort - (f64.const 12.34) - ) - ) - (drop - (call $abort - (f64.promote/f32 - (f32.const 56.779998779296875) + (drop + (call $abort + (f64.convert_s/i32 + (i32.const 55) + ) + ) + ) + (drop + (call $abort + (f64.const 0) + ) + ) + (drop + (call $abort + (f64.const 12.34) + ) + ) + (call $abort + (f64.promote/f32 + (f32.const 56.779998779296875) + ) ) ) ) @@ -720,16 +722,16 @@ ) ) (block i32 - (block - (block - (drop - (i32.const 4) - ) + (drop + (block i32 (drop - (i32.const 5) + (block i32 + (drop + (i32.const 4) + ) + (i32.const 5) + ) ) - ) - (drop (i32.const 6) ) ) @@ -784,20 +786,20 @@ ) ) (block i32 - (block - (block - (drop - (call $lb - (i32.const 4) - ) - ) + (drop + (block i32 (drop - (call $lb - (i32.const 5) + (block i32 + (drop + (call $lb + (i32.const 4) + ) + ) + (call $lb + (i32.const 5) + ) ) ) - ) - (drop (call $lb (i32.const 6) ) @@ -935,7 +937,6 @@ ) (br $do-once) ) - (nop) ) ) (func $dropCall (result i32) @@ -986,11 +987,11 @@ (func $usesSetGlobal2 (result i32) (return (block i32 - (block - (set_global $Int - (i32.const 40) - ) - (drop + (drop + (block i32 + (set_global $Int + (i32.const 40) + ) (get_global $Int) ) ) @@ -1020,7 +1021,6 @@ (i32.const 1337) ) ) - (nop) ) ) (func $ifChainEmpty (param $label i32) (result i32) @@ -1782,7 +1782,6 @@ ) (br $do-once) ) - (nop) ) (return) ) @@ -1806,7 +1805,6 @@ ) ) ) - (nop) ) (return) ) diff --git a/test/wasm-only.asm.js b/test/wasm-only.asm.js index 37119717356..fbdb727d851 100644 --- a/test/wasm-only.asm.js +++ b/test/wasm-only.asm.js @@ -24,6 +24,8 @@ function asm(global, env, buffer) { var _fabsf = env._fabsf; var do_i64 = env.do_i64; + var STACKTOP = 0; + function loads() { var i = 0, f = fround(0), d = +0; i = load1(100); @@ -284,6 +286,64 @@ function asm(global, env, buffer) { } return 44; } + + function propagateFallthrough() { + var x = 0; + x = 1; + x = x + 2 | 0; + x = x * 3 | 0; + while (1) { + if (1) { + if (x) { + return x | 0; + } else { + return 0; + } + } else { + return 1; + } + } + } + + function _pthread_mutex_lock(x) { x = x | 0; return 0; } + function _pthread_cond_wait(x, y) { x = x | 0; y = y | 0; return 0; } + + function __ZNSt3__211__call_onceERVmPvPFvS2_E($flag,$arg,$func) { + $flag = $flag|0; + $arg = $arg|0; + $func = $func|0; + var $0 = 0, $1 = 0, $cmp = 0, sp = 0; + sp = STACKTOP; + (_pthread_mutex_lock((21396|0))|0); + while(1) { + $0 = load4($flag); + $cmp = ($0|0)==(1); + if (!($cmp)) { + break; + } + (_pthread_cond_wait((21424|0),(21396|0))|0); // must be dropped properly + } + $1 = load4($flag); + return; + } + + function vii(x, y) { x = x | 0; y = y | 0; return; } + + function lua_switch($L) { + $L = $L|0; + var $call = 0; + $call = (_pthread_cond_wait($L,2)|0); + switch ($call|0) { + case 0: { + break; + } + default: { + (_pthread_cond_wait($L,11217)|0); + } + } + return 1; + } + function keepAlive() { loads(); stores(); @@ -297,6 +357,9 @@ function asm(global, env, buffer) { ifValue32(0, 0) | 0; switch64(i64(0)) | 0; unreachable_leftovers(0, 0, 0); + propagateFallthrough() | 0; + __ZNSt3__211__call_onceERVmPvPFvS2_E(0, 0, 0); + lua_switch(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 1a50a4385a1..9c7771a643e 100644 --- a/test/wasm-only.fromasm +++ b/test/wasm-only.fromasm @@ -367,6 +367,73 @@ ) ) ) + (func $propagateFallthrough (result i32) + (local $0 i32) + (select + (tee_local $0 + (i32.const 9) + ) + (i32.const 0) + (get_local $0) + ) + ) + (func $_pthread_mutex_lock (param $0 i32) (result i32) + (i32.const 0) + ) + (func $_pthread_cond_wait (param $0 i32) (param $1 i32) (result i32) + (i32.const 0) + ) + (func $__ZNSt3__211__call_onceERVmPvPFvS2_E (param $0 i32) (param $1 i32) (param $2 i32) + (drop + (call $_pthread_mutex_lock + (i32.const 21396) + ) + ) + (loop $while-in + (if + (i32.eq + (i32.load + (get_local $0) + ) + (i32.const 1) + ) + (block + (drop + (call $_pthread_cond_wait + (i32.const 21424) + (i32.const 21396) + ) + ) + (br $while-in) + ) + ) + ) + ) + (func $lua_switch (param $0 i32) (result i32) + (block $switch + (block $switch-default + (block $switch-case + (br_table $switch-case $switch-default + (i32.sub + (call $_pthread_cond_wait + (get_local $0) + (i32.const 2) + ) + (i32.const 0) + ) + ) + ) + (br $switch) + ) + (drop + (call $_pthread_cond_wait + (get_local $0) + (i32.const 11217) + ) + ) + ) + (i32.const 1) + ) (func $keepAlive (call $loads) (call $stores) @@ -412,6 +479,19 @@ (i32.const 0) (i32.const 0) ) + (drop + (call $propagateFallthrough) + ) + (call $__ZNSt3__211__call_onceERVmPvPFvS2_E + (i32.const 0) + (i32.const 0) + (i32.const 0) + ) + (drop + (call $lua_switch + (i32.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 b/test/wasm-only.fromasm.imprecise index b7994d6894a..2a8281991ed 100644 --- a/test/wasm-only.fromasm.imprecise +++ b/test/wasm-only.fromasm.imprecise @@ -295,6 +295,73 @@ ) ) ) + (func $propagateFallthrough (result i32) + (local $0 i32) + (select + (tee_local $0 + (i32.const 9) + ) + (i32.const 0) + (get_local $0) + ) + ) + (func $_pthread_mutex_lock (param $0 i32) (result i32) + (i32.const 0) + ) + (func $_pthread_cond_wait (param $0 i32) (param $1 i32) (result i32) + (i32.const 0) + ) + (func $__ZNSt3__211__call_onceERVmPvPFvS2_E (param $0 i32) (param $1 i32) (param $2 i32) + (drop + (call $_pthread_mutex_lock + (i32.const 21396) + ) + ) + (loop $while-in + (if + (i32.eq + (i32.load + (get_local $0) + ) + (i32.const 1) + ) + (block + (drop + (call $_pthread_cond_wait + (i32.const 21424) + (i32.const 21396) + ) + ) + (br $while-in) + ) + ) + ) + ) + (func $lua_switch (param $0 i32) (result i32) + (block $switch + (block $switch-default + (block $switch-case + (br_table $switch-case $switch-default + (i32.sub + (call $_pthread_cond_wait + (get_local $0) + (i32.const 2) + ) + (i32.const 0) + ) + ) + ) + (br $switch) + ) + (drop + (call $_pthread_cond_wait + (get_local $0) + (i32.const 11217) + ) + ) + ) + (i32.const 1) + ) (func $keepAlive (call $loads) (call $stores) @@ -340,6 +407,19 @@ (i32.const 0) (i32.const 0) ) + (drop + (call $propagateFallthrough) + ) + (call $__ZNSt3__211__call_onceERVmPvPFvS2_E + (i32.const 0) + (i32.const 0) + (i32.const 0) + ) + (drop + (call $lua_switch + (i32.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 4e05d6bd354..de143e5841e 100644 --- a/test/wasm-only.fromasm.imprecise.no-opts +++ b/test/wasm-only.fromasm.imprecise.no-opts @@ -18,6 +18,7 @@ (import "env" "table" (table 3 3 anyfunc)) (import "env" "memoryBase" (global $memoryBase i32)) (import "env" "tableBase" (global $tableBase i32)) + (global $STACKTOP (mut i32) (i32.const 0)) (global $tempRet0 (mut i32) (i32.const 0)) (elem (get_global $tableBase) $legalfunc$illegalImport $legalfunc$_fabsf $legalfunc$do_i64) (export "test64" (func $test64)) @@ -886,6 +887,135 @@ (i32.const 44) ) ) + (func $propagateFallthrough (result i32) + (local $x i32) + (set_local $x + (i32.const 1) + ) + (set_local $x + (i32.add + (get_local $x) + (i32.const 2) + ) + ) + (set_local $x + (i32.mul + (get_local $x) + (i32.const 3) + ) + ) + (loop $while-in i32 + (block $while-out i32 + (if i32 + (i32.const 1) + (if i32 + (get_local $x) + (return + (get_local $x) + ) + (return + (i32.const 0) + ) + ) + (return + (i32.const 1) + ) + ) + ) + ) + ) + (func $_pthread_mutex_lock (param $x i32) (result i32) + (return + (i32.const 0) + ) + ) + (func $_pthread_cond_wait (param $x i32) (param $y i32) (result i32) + (return + (i32.const 0) + ) + ) + (func $__ZNSt3__211__call_onceERVmPvPFvS2_E (param $$flag i32) (param $$arg i32) (param $$func i32) + (local $$0 i32) + (local $$1 i32) + (local $$cmp i32) + (local $sp i32) + (set_local $sp + (get_global $STACKTOP) + ) + (drop + (call $_pthread_mutex_lock + (i32.const 21396) + ) + ) + (loop $while-in + (block $while-out + (set_local $$0 + (i32.load + (get_local $$flag) + ) + ) + (set_local $$cmp + (i32.eq + (get_local $$0) + (i32.const 1) + ) + ) + (if + (i32.eqz + (get_local $$cmp) + ) + (br $while-out) + ) + (drop + (call $_pthread_cond_wait + (i32.const 21424) + (i32.const 21396) + ) + ) + (br $while-in) + ) + ) + (set_local $$1 + (i32.load + (get_local $$flag) + ) + ) + (return) + ) + (func $vii (param $x i32) (param $y i32) + (return) + ) + (func $lua_switch (param $$L i32) (result i32) + (local $$call i32) + (set_local $$call + (call $_pthread_cond_wait + (get_local $$L) + (i32.const 2) + ) + ) + (block $switch + (block $switch-default + (block $switch-case + (br_table $switch-case $switch-default + (i32.sub + (get_local $$call) + (i32.const 0) + ) + ) + ) + (br $switch) + ) + (drop + (call $_pthread_cond_wait + (get_local $$L) + (i32.const 11217) + ) + ) + ) + (return + (i32.const 1) + ) + ) (func $keepAlive (call $loads) (call $stores) @@ -931,6 +1061,19 @@ (i32.const 0) (i32.const 0) ) + (drop + (call $propagateFallthrough) + ) + (call $__ZNSt3__211__call_onceERVmPvPFvS2_E + (i32.const 0) + (i32.const 0) + (i32.const 0) + ) + (drop + (call $lua_switch + (i32.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 eae9c459724..fe713820db9 100644 --- a/test/wasm-only.fromasm.no-opts +++ b/test/wasm-only.fromasm.no-opts @@ -18,6 +18,7 @@ (import "env" "table" (table 3 3 anyfunc)) (import "env" "memoryBase" (global $memoryBase i32)) (import "env" "tableBase" (global $tableBase i32)) + (global $STACKTOP (mut i32) (i32.const 0)) (global $tempRet0 (mut i32) (i32.const 0)) (elem (get_global $tableBase) $legalfunc$illegalImport $legalfunc$_fabsf $legalfunc$do_i64) (export "test64" (func $test64)) @@ -934,6 +935,135 @@ (i32.const 44) ) ) + (func $propagateFallthrough (result i32) + (local $x i32) + (set_local $x + (i32.const 1) + ) + (set_local $x + (i32.add + (get_local $x) + (i32.const 2) + ) + ) + (set_local $x + (i32.mul + (get_local $x) + (i32.const 3) + ) + ) + (loop $while-in i32 + (block $while-out i32 + (if i32 + (i32.const 1) + (if i32 + (get_local $x) + (return + (get_local $x) + ) + (return + (i32.const 0) + ) + ) + (return + (i32.const 1) + ) + ) + ) + ) + ) + (func $_pthread_mutex_lock (param $x i32) (result i32) + (return + (i32.const 0) + ) + ) + (func $_pthread_cond_wait (param $x i32) (param $y i32) (result i32) + (return + (i32.const 0) + ) + ) + (func $__ZNSt3__211__call_onceERVmPvPFvS2_E (param $$flag i32) (param $$arg i32) (param $$func i32) + (local $$0 i32) + (local $$1 i32) + (local $$cmp i32) + (local $sp i32) + (set_local $sp + (get_global $STACKTOP) + ) + (drop + (call $_pthread_mutex_lock + (i32.const 21396) + ) + ) + (loop $while-in + (block $while-out + (set_local $$0 + (i32.load + (get_local $$flag) + ) + ) + (set_local $$cmp + (i32.eq + (get_local $$0) + (i32.const 1) + ) + ) + (if + (i32.eqz + (get_local $$cmp) + ) + (br $while-out) + ) + (drop + (call $_pthread_cond_wait + (i32.const 21424) + (i32.const 21396) + ) + ) + (br $while-in) + ) + ) + (set_local $$1 + (i32.load + (get_local $$flag) + ) + ) + (return) + ) + (func $vii (param $x i32) (param $y i32) + (return) + ) + (func $lua_switch (param $$L i32) (result i32) + (local $$call i32) + (set_local $$call + (call $_pthread_cond_wait + (get_local $$L) + (i32.const 2) + ) + ) + (block $switch + (block $switch-default + (block $switch-case + (br_table $switch-case $switch-default + (i32.sub + (get_local $$call) + (i32.const 0) + ) + ) + ) + (br $switch) + ) + (drop + (call $_pthread_cond_wait + (get_local $$L) + (i32.const 11217) + ) + ) + ) + (return + (i32.const 1) + ) + ) (func $keepAlive (call $loads) (call $stores) @@ -979,6 +1109,19 @@ (i32.const 0) (i32.const 0) ) + (drop + (call $propagateFallthrough) + ) + (call $__ZNSt3__211__call_onceERVmPvPFvS2_E + (i32.const 0) + (i32.const 0) + (i32.const 0) + ) + (drop + (call $lua_switch + (i32.const 0) + ) + ) ) (func $__emscripten_dceable_type_decls (drop