From be0a16d5c9bb99e345a86f15110e0970efd92b8a Mon Sep 17 00:00:00 2001 From: Alexander Guryanov Date: Sun, 2 Nov 2025 23:51:12 +0300 Subject: [PATCH 01/15] Add partial support for -fwasm-exceptions in Asyncify (#5343) --- src/passes/Asyncify.cpp | 283 ++++- ...serts_pass-arg=asyncify-onlylist@waka.wast | 21 +- ...asyncify_pass-arg=asyncify-eh-asserts.wast | 1091 +++++++++++++++++ .../asyncify_pass-arg=asyncify-eh-ignore.wast | 315 +++++ .../passes/asyncify_pass-arg=asyncify-eh.wast | 242 ++++ 5 files changed, 1942 insertions(+), 10 deletions(-) create mode 100644 test/lit/passes/asyncify_pass-arg=asyncify-eh-asserts.wast create mode 100644 test/lit/passes/asyncify_pass-arg=asyncify-eh-ignore.wast create mode 100644 test/lit/passes/asyncify_pass-arg=asyncify-eh.wast diff --git a/src/passes/Asyncify.cpp b/src/passes/Asyncify.cpp index 0f30e4eecb9..d200974a0f8 100644 --- a/src/passes/Asyncify.cpp +++ b/src/passes/Asyncify.cpp @@ -96,6 +96,13 @@ // Overall, this should allow good performance with small overhead that is // mostly noticed at rewind time. // +// Exceptions handling (-fwasm-exceptions) is partially supported. Asyncify +// can't start unwind operation when a catch block is in the stack trace. +// If assertions mode is enabled then pass will check if unwind called from +// within catch block or not, and if so throw an unreachable exception. +// If "ignore unwind from catch" mode is enable then Asyncify will skip +// any unwind call from within catch block. +// // After this pass is run a new i32 global "__asyncify_state" is added, which // has the following values: // @@ -239,6 +246,12 @@ // an unwind/rewind in an invalid place (this can be helpful for manual // tweaking of the only-list / remove-list, see later). // +// --pass-arg=asyncify-ignore-unwind-from-catch +// +// This enables additional check to be performed before unwinding. In +// cases where the unwind operation is triggered from the catch block, +// it will be silently ignored (-fwasm-exceptions support) +// // --pass-arg=asyncify-verbose // // Logs out instrumentation decisions to the console. This can help figure @@ -317,13 +330,16 @@ #include "asmjs/shared-constants.h" #include "cfg/liveness-traversal.h" +#include "ir/branch-utils.h" #include "ir/effects.h" +#include "ir/eh-utils.h" #include "ir/find_all.h" #include "ir/linear-execution.h" #include "ir/literal-utils.h" #include "ir/memory-utils.h" #include "ir/module-utils.h" #include "ir/names.h" +#include "ir/parents.h" #include "ir/utils.h" #include "pass.h" #include "passes/pass-utils.h" @@ -338,6 +354,8 @@ namespace { static const Name ASYNCIFY_STATE = "__asyncify_state"; static const Name ASYNCIFY_GET_STATE = "asyncify_get_state"; +static const Name ASYNCIFY_CATCH_COUNTER = "__asyncify_catch_counter"; +static const Name ASYNCIFY_GET_CATCH_COUNTER = "asyncify_get_catch_counter"; static const Name ASYNCIFY_DATA = "__asyncify_data"; static const Name ASYNCIFY_START_UNWIND = "asyncify_start_unwind"; static const Name ASYNCIFY_STOP_UNWIND = "asyncify_stop_unwind"; @@ -1138,6 +1156,18 @@ struct AsyncifyFlow : public Pass { // here as well. results.push_back(makeCallSupport(curr)); continue; + } else if (auto* try_ = curr->dynCast()) { + if (item.phase == Work::Scan) { + work.push_back(Work{curr, Work::Finish}); + work.push_back(Work{try_->body, Work::Scan}); + // catchBodies are ignored because we assume that pause/resume will + // not happen inside them + continue; + } + try_->body = results.back(); + results.pop_back(); + results.push_back(try_); + continue; } // We must handle all control flow above, and all things that can change // the state, so there should be nothing that can reach here - add it @@ -1218,6 +1248,202 @@ struct AsyncifyFlow : public Pass { } }; +// Add catch block counters to verify that unwind is not called from catch +// block. +struct AsyncifyAddCatchCounters : public Pass { + bool isFunctionParallel() override { return true; } + + std::unique_ptr create() override { + return std::make_unique(); + } + + void runOnFunction(Module* module_, Function* func) override { + class CountersBuilder : public Builder { + public: + CountersBuilder(Module& wasm) : Builder(wasm) {} + Expression* makeInc(int amount = 1) { + return makeGlobalSet( + ASYNCIFY_CATCH_COUNTER, + makeBinary(AddInt32, + makeGlobalGet(ASYNCIFY_CATCH_COUNTER, Type::i32), + makeConst(int32_t(amount)))); + } + Expression* makeDec(int amount = 1) { + return makeGlobalSet( + ASYNCIFY_CATCH_COUNTER, + makeBinary(SubInt32, + makeGlobalGet(ASYNCIFY_CATCH_COUNTER, Type::i32), + makeConst(int32_t(amount)))); + } + }; + + // with this walker we will handle those changes of counter: + // - entering top-level catch (= pop) +1 + // - entering nested catch (= pop) 0 (ignored) + // + // - return inside top-level/nested catch -1 + // - return outside top-level/nested catch 0 (ignored) + // + // - break target outside of top-level catch -1 + // - break target inside of top-level catch 0 (ignored) + // - break outside top-level/nested catch 0 (ignored) + // + // - exiting from top-level catch -1 + // - exiting from nested catch 0 (ignored) + struct AddCountersWalker : public PostWalker { + Function* func; + CountersBuilder* builder; + BranchUtils::BranchTargets* branchTargets; + Parents* parents; + int finallyNum = 0; + int popNum = 0; + + int getCatchCount(Expression* expression) { + int catchCount = 0; + while (expression != func->body) { + auto parent = parents->getParent(expression); + if (auto* try_ = parent->dynCast()) { + if (try_->body != expression) { + catchCount++; + } + } + expression = parent; + } + + return catchCount; + } + + // Each catch block except catch_all should have pop instruction + // We increment counter each time when we enter top-level catch block + void visitPop(Pop* pop) { + if (getCatchCount(pop) == 1) { + auto name = + func->name.toString() + "-pop-" + std::to_string(++popNum); + replaceCurrent( + builder->makeBlock(name, {pop, builder->makeInc()}, Type::none)); + } + } + void visitLocalSet(LocalSet* set) { + auto block = set->value->dynCast(); // from visitPop above + if (block && block->name.hasSubstring("-pop-")) { + auto pop = block->list[0]->dynCast(); + assert(pop && getCatchCount(pop) == 1); + set->value = pop; + replaceCurrent(builder->makeBlock( + block->name, {set, builder->makeInc()}, Type::none)); + } + } + + // When return happens we decrement counter on 1, because we account + // only top-level catch blocks + // catch + // +1 + // catch + // ;; not counted + // -1 + // return + // ... + void visitReturn(Return* ret) { + if (getCatchCount(ret) > 0) { + replaceCurrent(builder->makeSequence(builder->makeDec(), ret)); + } + } + + // When break happens we decrement counter only if it goes out + // from top-level catch block + void visitBreak(Break* br) { + Expression* target = branchTargets->getTarget(br->name); + assert(target != nullptr); + if (getCatchCount(br) > 0 && getCatchCount(target) == 0) { + if (br->condition == nullptr) { + replaceCurrent(builder->makeSequence(builder->makeDec(), br)); + } else if (br->value == nullptr) { + auto decIf = + builder->makeIf(br->condition, + builder->makeSequence(builder->makeDec(), br), + nullptr); + br->condition = nullptr; + replaceCurrent(decIf); + } else { + Index newLocal = builder->addVar(func, br->value->type); + auto setLocal = builder->makeLocalSet(newLocal, br->value); + auto getLocal = builder->makeLocalGet(newLocal, br->value->type); + auto condition = br->condition; + br->condition = nullptr; + br->value = getLocal; + auto decIf = + builder->makeIf(condition, + builder->makeSequence(builder->makeDec(), br), + getLocal); + replaceCurrent(builder->makeSequence(setLocal, decIf)); + } + } + } + + // Replacing each top-level catch block with try/catch_all(finally) and + // increase counter for catch_all blocks (not handled by visitPop); dec + // counter at the end of catch block try ({fn}-finally-{label}) + // +1 + // {catch body} + // -1 + // catch_all + // -1 + // rethrow {fn}-finally-{label} + void visitTry(Try* curr) { + if (getCatchCount(curr) == 0) { + for (size_t i = 0; i < curr->catchBodies.size(); ++i) { + curr->catchBodies[i] = addCatchCounters( + curr->catchBodies[i], i == curr->catchTags.size()); + } + } + } + Expression* addCatchCounters(Expression* expression, bool catchAll) { + auto block = expression->dynCast(); + if (block == nullptr) { + block = builder->makeBlock(expression); + } + + // catch_all case is not covered by visitPop + if (catchAll) { + block->list.insertAt(0, builder->makeInc()); + } + + // dec counters at the end of catch + if (block->type == Type::none) { + auto last = block->list[block->list.size() - 1]; + if (!last->dynCast()) { + block->list.push_back(builder->makeDec()); + block->finalize(); + } + } + + auto name = + func->name.toString() + "-finally-" + std::to_string(++finallyNum); + return builder->makeTry( + name, + block, + {}, + {builder->makeSequence(builder->makeDec(), + builder->makeRethrow(name))}, + block->type); + } + }; + + Parents parents(func->body); + CountersBuilder builder(*module_); + BranchUtils::BranchTargets branchTargets(func->body); + + AddCountersWalker addCountersWalker; + addCountersWalker.func = func; + addCountersWalker.builder = &builder; + addCountersWalker.branchTargets = &branchTargets; + addCountersWalker.parents = &parents; + addCountersWalker.walk(func->body); + + EHUtils::handleBlockNestedPops(func, *module_); + } +}; + // Add asserts in non-instrumented code. struct AsyncifyAssertInNonInstrumented : public Pass { bool isFunctionParallel() override { return true; } @@ -1650,6 +1876,8 @@ struct Asyncify : public Pass { auto relocatable = hasArgument("asyncify-relocatable"); auto secondaryMemory = hasArgument("asyncify-in-secondary-memory"); auto propagateAddList = hasArgument("asyncify-propagate-addlist"); + auto ignoreCatchUnwind = hasArgument("asyncify-ignore-unwind-from-catch"); + auto addAsyncifyCounters = asserts || ignoreCatchUnwind; // Ensure there is a memory, as we need it. @@ -1714,7 +1942,7 @@ struct Asyncify : public Pass { verbose); // Add necessary globals before we emit code to use them. - addGlobals(module, relocatable); + addGlobals(module, relocatable, addAsyncifyCounters); // Compute the set of functions we will instrument. All of the passes we run // below only need to run there. @@ -1755,12 +1983,17 @@ struct Asyncify : public Pass { runner.setValidateGlobally(false); runner.run(); } - if (asserts) { + if (asserts || addAsyncifyCounters) { // Add asserts in non-instrumented code. Note we do not use an // instrumented pass runner here as we do want to run on all functions. PassRunner runner(module); - runner.add(std::make_unique( - &analyzer, pointerType, asyncifyMemory)); + if (addAsyncifyCounters) { + runner.add(std::make_unique()); + } + if (asserts) { + runner.add(std::make_unique( + &analyzer, pointerType, asyncifyMemory)); + } runner.setIsNested(true); runner.setValidateGlobally(false); runner.run(); @@ -1786,11 +2019,11 @@ struct Asyncify : public Pass { } // Finally, add function support (that should not have been seen by // the previous passes). - addFunctions(module); + addFunctions(module, asserts, ignoreCatchUnwind); } private: - void addGlobals(Module* module, bool imported) { + void addGlobals(Module* module, bool imported, bool addAsyncifyCounters) { Builder builder(*module); auto asyncifyState = builder.makeGlobal(ASYNCIFY_STATE, @@ -1803,6 +2036,19 @@ struct Asyncify : public Pass { } module->addGlobal(std::move(asyncifyState)); + if (addAsyncifyCounters) { + auto asyncifyCatchCounter = + builder.makeGlobal(ASYNCIFY_CATCH_COUNTER, + Type::i32, + builder.makeConst(int32_t(0)), + Builder::Mutable); + if (imported) { + asyncifyCatchCounter->module = ENV; + asyncifyCatchCounter->base = ASYNCIFY_CATCH_COUNTER; + } + module->addGlobal(std::move(asyncifyCatchCounter)); + } + auto asyncifyData = builder.makeGlobal(ASYNCIFY_DATA, pointerType, builder.makeConst(pointerType), @@ -1814,14 +2060,24 @@ struct Asyncify : public Pass { module->addGlobal(std::move(asyncifyData)); } - void addFunctions(Module* module) { + void addFunctions(Module* module, bool asserts, bool ignoreCatchUnwind) { Builder builder(*module); auto makeFunction = [&](Name name, bool setData, State state) { + auto* body = builder.makeBlock(); + if (name == ASYNCIFY_START_UNWIND && (asserts || ignoreCatchUnwind)) { + auto* check = builder.makeIf( + builder.makeBinary( + NeInt32, + builder.makeGlobalGet(ASYNCIFY_CATCH_COUNTER, Type::i32), + builder.makeConst(int32_t(0))), + ignoreCatchUnwind ? (Expression*)builder.makeReturn() + : (Expression*)builder.makeUnreachable()); + body->list.push_back(check); + } std::vector params; if (setData) { params.push_back(pointerType); } - auto* body = builder.makeBlock(); body->list.push_back(builder.makeGlobalSet( ASYNCIFY_STATE, builder.makeConst(int32_t(state)))); if (setData) { @@ -1869,6 +2125,17 @@ struct Asyncify : public Pass { builder.makeGlobalGet(ASYNCIFY_STATE, Type::i32))); module->addExport(builder.makeExport( ASYNCIFY_GET_STATE, ASYNCIFY_GET_STATE, ExternalKind::Function)); + + if (asserts || ignoreCatchUnwind) { + module->addFunction(builder.makeFunction( + ASYNCIFY_GET_CATCH_COUNTER, + Signature(Type::none, Type::i32), + {}, + builder.makeGlobalGet(ASYNCIFY_CATCH_COUNTER, Type::i32))); + module->addExport(builder.makeExport(ASYNCIFY_GET_CATCH_COUNTER, + ASYNCIFY_GET_CATCH_COUNTER, + ExternalKind::Function)); + } } Name createSecondaryMemory(Module* module, Address secondaryMemorySize) { diff --git a/test/lit/passes/asyncify_pass-arg=asyncify-asserts_pass-arg=asyncify-onlylist@waka.wast b/test/lit/passes/asyncify_pass-arg=asyncify-asserts_pass-arg=asyncify-onlylist@waka.wast index d5dc9ff9bb4..1aa4462de01 100644 --- a/test/lit/passes/asyncify_pass-arg=asyncify-asserts_pass-arg=asyncify-onlylist@waka.wast +++ b/test/lit/passes/asyncify_pass-arg=asyncify-asserts_pass-arg=asyncify-onlylist@waka.wast @@ -11,9 +11,9 @@ (module ;; CHECK: (type $f (func)) (type $f (func)) - ;; CHECK: (type $1 (func (param i32))) + ;; CHECK: (type $1 (func (result i32))) - ;; CHECK: (type $2 (func (result i32))) + ;; CHECK: (type $2 (func (param i32))) ;; CHECK: (import "env" "import" (func $import)) (import "env" "import" (func $import)) @@ -27,6 +27,8 @@ (table funcref (elem $calls-import2-drop $calls-import2-drop)) ;; CHECK: (global $__asyncify_state (mut i32) (i32.const 0)) + ;; CHECK: (global $__asyncify_catch_counter (mut i32) (i32.const 0)) + ;; CHECK: (global $__asyncify_data (mut i32) (i32.const 0)) ;; CHECK: (memory $0 1 2) @@ -45,6 +47,8 @@ ;; CHECK: (export "asyncify_get_state" (func $asyncify_get_state)) + ;; CHECK: (export "asyncify_get_catch_counter" (func $asyncify_get_catch_counter)) + ;; CHECK: (func $calls-import ;; CHECK-NEXT: (local $0 i32) ;; CHECK-NEXT: (local.set $0 @@ -154,6 +158,15 @@ ) ;; CHECK: (func $asyncify_start_unwind (param $0 i32) +;; CHECK-NEXT: (if +;; CHECK-NEXT: (i32.ne +;; CHECK-NEXT: (global.get $__asyncify_catch_counter) +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (then +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) ;; CHECK-NEXT: (global.set $__asyncify_state ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) @@ -238,3 +251,7 @@ ;; CHECK: (func $asyncify_get_state (result i32) ;; CHECK-NEXT: (global.get $__asyncify_state) ;; CHECK-NEXT: ) + +;; CHECK: (func $asyncify_get_catch_counter (result i32) +;; CHECK-NEXT: (global.get $__asyncify_catch_counter) +;; CHECK-NEXT: ) diff --git a/test/lit/passes/asyncify_pass-arg=asyncify-eh-asserts.wast b/test/lit/passes/asyncify_pass-arg=asyncify-eh-asserts.wast new file mode 100644 index 00000000000..1bc520c4949 --- /dev/null +++ b/test/lit/passes/asyncify_pass-arg=asyncify-eh-asserts.wast @@ -0,0 +1,1091 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. +;; NOTE: This test was ported using port_passes_tests_to_lit.py and could be cleaned up. + +;; RUN: foreach %s %t wasm-opt --enable-exception-handling --asyncify --pass-arg=asyncify-asserts --pass-arg=asyncify-onlylist@level0,level1 -S -o - | filecheck %s + +(module + ;; CHECK: (type $0 (func)) + + ;; CHECK: (type $1 (func (param i32))) + + ;; CHECK: (type $2 (func (result i32))) + + ;; CHECK: (import "extmod" "exttag" (tag $default (type $1) (param i32))) + (import "extmod" "exttag" (tag $default (param i32))) + ;; CHECK: (import "extmod" "exttag" (tag $ret (type $1) (param i32))) + (import "extmod" "exttag" (tag $ret (param i32))) + ;; CHECK: (import "extmod" "exttag" (tag $brif (type $1) (param i32))) + (import "extmod" "exttag" (tag $brif (param i32))) + + (memory 1 2) + + ;; CHECK: (global $__asyncify_state (mut i32) (i32.const 0)) + + ;; CHECK: (global $__asyncify_catch_counter (mut i32) (i32.const 0)) + + ;; CHECK: (global $__asyncify_data (mut i32) (i32.const 0)) + + ;; CHECK: (memory $0 1 2) + + ;; CHECK: (export "asyncify_start_unwind" (func $asyncify_start_unwind)) + + ;; CHECK: (export "asyncify_stop_unwind" (func $asyncify_stop_unwind)) + + ;; CHECK: (export "asyncify_start_rewind" (func $asyncify_start_rewind)) + + ;; CHECK: (export "asyncify_stop_rewind" (func $asyncify_stop_rewind)) + + ;; CHECK: (export "asyncify_get_state" (func $asyncify_get_state)) + + ;; CHECK: (export "asyncify_get_catch_counter" (func $asyncify_get_catch_counter)) + + ;; CHECK: (func $level0 + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (local $2 i32) + ;; CHECK-NEXT: (local $3 i32) + ;; CHECK-NEXT: (local $4 i32) + ;; CHECK-NEXT: (local $5 i32) + ;; CHECK-NEXT: (local $6 i32) + ;; CHECK-NEXT: (local $7 i32) + ;; CHECK-NEXT: (local $8 i32) + ;; CHECK-NEXT: (local $9 i32) + ;; CHECK-NEXT: (local $10 i32) + ;; CHECK-NEXT: (local $11 i32) + ;; CHECK-NEXT: (local $12 i32) + ;; CHECK-NEXT: (local $13 i32) + ;; CHECK-NEXT: (local $14 i32) + ;; CHECK-NEXT: (local $15 i32) + ;; CHECK-NEXT: (local $16 i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.tee $15 + ;; CHECK-NEXT: (block $__asyncify_unwind + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const -4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $16 + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (block $label + ;; CHECK-NEXT: (try $try1 + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br_if $label + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (local.set $3 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $4 + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $4) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $fn1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch $default + ;; CHECK-NEXT: (local.set $12 + ;; CHECK-NEXT: (pop i32) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (try $level0-finally-1 + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (block $level0-pop-1 + ;; CHECK-NEXT: (local.set $5 + ;; CHECK-NEXT: (local.get $12) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (local.get $5) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $fn2) + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (rethrow $level0-finally-1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch $ret + ;; CHECK-NEXT: (local.set $13 + ;; CHECK-NEXT: (pop i32) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (try $level0-finally-2 + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (block $level0-pop-2 + ;; CHECK-NEXT: (local.set $6 + ;; CHECK-NEXT: (local.get $13) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (local.get $6) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (local.set $7 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $8 + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $7) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $8) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $fn2) + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (rethrow $level0-finally-2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch $brif + ;; CHECK-NEXT: (local.set $14 + ;; CHECK-NEXT: (pop i32) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (try $level0-finally-3 + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (block $level0-pop-3 + ;; CHECK-NEXT: (local.set $9 + ;; CHECK-NEXT: (local.get $14) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (local.get $9) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $10 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $11 + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $11) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $label) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $fn2) + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (rethrow $level0-finally-3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (try $level0-finally-4 + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $fn3) + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (rethrow $level0-finally-4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $15) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $level0 + (local $0 i32) + (try $try1 + (do + (br_if $try1 + (i32.eqz + (local.get $0) + ) + ) + (if + (i32.eqz + (local.get $0) + ) + (then + (return) + ) + ) + (call $fn1) + ) + (catch $default + (local.set $0 + (pop i32) + ) + (call $fn2) + ) + (catch $ret + (local.set $0 + (pop i32) + ) + (if + (i32.eqz + (local.get $0) + ) + (then + (return) + ) + ) + (call $fn2) + ) + (catch $brif + (local.set $0 + (pop i32) + ) + (br_if $try1 + (i32.eqz + (local.get $0) + ) + ) + (call $fn2) + ) + (catch_all + (call $fn3) + ) + ) + ) + + ;; CHECK: (func $level1 + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (local $2 i32) + ;; CHECK-NEXT: (local $3 i32) + ;; CHECK-NEXT: (local $4 i32) + ;; CHECK-NEXT: (local $5 i32) + ;; CHECK-NEXT: (local $6 i32) + ;; CHECK-NEXT: (local $7 i32) + ;; CHECK-NEXT: (local $8 i32) + ;; CHECK-NEXT: (local $9 i32) + ;; CHECK-NEXT: (local $10 i32) + ;; CHECK-NEXT: (local $11 i32) + ;; CHECK-NEXT: (local $12 i32) + ;; CHECK-NEXT: (local $13 i32) + ;; CHECK-NEXT: (local $14 i32) + ;; CHECK-NEXT: (local $15 i32) + ;; CHECK-NEXT: (local $16 i32) + ;; CHECK-NEXT: (local $17 i32) + ;; CHECK-NEXT: (local $18 i32) + ;; CHECK-NEXT: (local $19 i32) + ;; CHECK-NEXT: (local $20 i32) + ;; CHECK-NEXT: (local $21 i32) + ;; CHECK-NEXT: (local $22 i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.tee $21 + ;; CHECK-NEXT: (block $__asyncify_unwind + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const -4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $22 + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (block $label + ;; CHECK-NEXT: (try $try1 + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (try + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br_if $label + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (local.set $3 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $4 + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $4) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $fn1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (try $level1-finally-1 + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $fn2) + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (rethrow $level1-finally-1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $fn3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch $default + ;; CHECK-NEXT: (local.set $18 + ;; CHECK-NEXT: (pop i32) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (try $level1-finally-2 + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (block $level1-pop-1 + ;; CHECK-NEXT: (local.set $5 + ;; CHECK-NEXT: (local.get $18) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (local.get $5) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (try + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (call $fn1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (call $fn3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $fn2) + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (rethrow $level1-finally-2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch $ret + ;; CHECK-NEXT: (local.set $19 + ;; CHECK-NEXT: (pop i32) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (try $level1-finally-3 + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (block $level1-pop-2 + ;; CHECK-NEXT: (local.set $6 + ;; CHECK-NEXT: (local.get $19) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (local.get $6) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (try + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (call $fn1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (local.set $7 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $8 + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $7) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $8) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $fn3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (local.set $9 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $10 + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $9) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $10) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $fn2) + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (rethrow $level1-finally-3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch $brif + ;; CHECK-NEXT: (local.set $20 + ;; CHECK-NEXT: (pop i32) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (try $level1-finally-4 + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (block $level1-pop-3 + ;; CHECK-NEXT: (local.set $11 + ;; CHECK-NEXT: (local.get $20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (local.get $11) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $nottop + ;; CHECK-NEXT: (try + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (call $fn1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (local.set $12 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $13 + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $12) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br_if $nottop + ;; CHECK-NEXT: (local.get $13) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $14 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $15 + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $14) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $15) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $label) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $fn3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $16 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $17 + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $16) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $17) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $label) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $fn2) + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (rethrow $level1-finally-4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (try $level1-finally-5 + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (try + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (call $fn1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (call $fn2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $fn3) + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (rethrow $level1-finally-5) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $21) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $level1 + (local $0 i32) + (try $try1 + (do + (try + (do + (br_if $try1 + (i32.eqz + (local.get $0) + ) + ) + (if + (i32.eqz + (local.get $0) + ) + (then + (return) + ) + ) + (call $fn1) + ) + (catch_all + (call $fn2) + ) + ) + (call $fn3) + ) + (catch $default + (local.set $0 + (pop i32) + ) + (try + (do + (call $fn1) + ) + (catch_all + (call $fn3) + ) + ) + (call $fn2) + ) + (catch $ret + (local.set $0 + (pop i32) + ) + (try + (do + (call $fn1) + ) + (catch_all + (if + (i32.eqz + (local.get $0) + ) + (then + (return) + ) + ) + (call $fn3) + ) + ) + (if + (i32.eqz + (local.get $0) + ) + (then + (return) + ) + ) + (call $fn2) + ) + (catch $brif + (local.set $0 + (pop i32) + ) + (block $nottop + (try + (do + (call $fn1) + ) + (catch_all + (br_if $nottop + (i32.eqz + (local.get $0) + ) + ) + (br_if $try1 + (i32.eqz + (local.get $0) + ) + ) + (call $fn3) + ) + ) + ) + (br_if $try1 + (i32.eqz + (local.get $0) + ) + ) + (call $fn2) + ) + (catch_all + (try + (do + (call $fn1) + ) + (catch_all + (call $fn2) + ) + ) + (call $fn3) + ) + ) + ) + ;; CHECK: (func $fn1 + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $fn1 + ) + + ;; CHECK: (func $fn2 + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $fn2 + ) + + ;; CHECK: (func $fn3 + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $fn3 + ) +) +;; CHECK: (func $asyncify_start_unwind (param $0 i32) +;; CHECK-NEXT: (if +;; CHECK-NEXT: (i32.ne +;; CHECK-NEXT: (global.get $__asyncify_catch_counter) +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (then +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (global.set $__asyncify_state +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (global.set $__asyncify_data +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (if +;; CHECK-NEXT: (i32.gt_u +;; CHECK-NEXT: (i32.load +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (i32.load offset=4 +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (then +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $asyncify_stop_unwind +;; CHECK-NEXT: (global.set $__asyncify_state +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (if +;; CHECK-NEXT: (i32.gt_u +;; CHECK-NEXT: (i32.load +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (i32.load offset=4 +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (then +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $asyncify_start_rewind (param $0 i32) +;; CHECK-NEXT: (global.set $__asyncify_state +;; CHECK-NEXT: (i32.const 2) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (global.set $__asyncify_data +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (if +;; CHECK-NEXT: (i32.gt_u +;; CHECK-NEXT: (i32.load +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (i32.load offset=4 +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (then +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $asyncify_stop_rewind +;; CHECK-NEXT: (global.set $__asyncify_state +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (if +;; CHECK-NEXT: (i32.gt_u +;; CHECK-NEXT: (i32.load +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (i32.load offset=4 +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (then +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $asyncify_get_state (result i32) +;; CHECK-NEXT: (global.get $__asyncify_state) +;; CHECK-NEXT: ) + +;; CHECK: (func $asyncify_get_catch_counter (result i32) +;; CHECK-NEXT: (global.get $__asyncify_catch_counter) +;; CHECK-NEXT: ) diff --git a/test/lit/passes/asyncify_pass-arg=asyncify-eh-ignore.wast b/test/lit/passes/asyncify_pass-arg=asyncify-eh-ignore.wast new file mode 100644 index 00000000000..092c3b830a6 --- /dev/null +++ b/test/lit/passes/asyncify_pass-arg=asyncify-eh-ignore.wast @@ -0,0 +1,315 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. +;; NOTE: This test was ported using port_passes_tests_to_lit.py and could be cleaned up. + +;; RUN: foreach %s %t wasm-opt --enable-exception-handling --asyncify --pass-arg=asyncify-ignore-unwind-from-catch --pass-arg=asyncify-onlylist@foo -S -o - | filecheck %s + +(module + ;; CHECK: (type $0 (func)) + + ;; CHECK: (type $1 (func (param i32))) + + ;; CHECK: (type $2 (func (result i32))) + + ;; CHECK: (import "extmod" "exttag" (tag $default (type $1) (param i32))) + (import "extmod" "exttag" (tag $default (param i32))) + + (memory 1 2) + + ;; CHECK: (global $__asyncify_state (mut i32) (i32.const 0)) + + ;; CHECK: (global $__asyncify_catch_counter (mut i32) (i32.const 0)) + + ;; CHECK: (global $__asyncify_data (mut i32) (i32.const 0)) + + ;; CHECK: (memory $0 1 2) + + ;; CHECK: (export "asyncify_start_unwind" (func $asyncify_start_unwind)) + + ;; CHECK: (export "asyncify_stop_unwind" (func $asyncify_stop_unwind)) + + ;; CHECK: (export "asyncify_start_rewind" (func $asyncify_start_rewind)) + + ;; CHECK: (export "asyncify_stop_rewind" (func $asyncify_stop_rewind)) + + ;; CHECK: (export "asyncify_get_state" (func $asyncify_get_state)) + + ;; CHECK: (export "asyncify_get_catch_counter" (func $asyncify_get_catch_counter)) + + ;; CHECK: (func $foo + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (local $2 i32) + ;; CHECK-NEXT: (local $3 i32) + ;; CHECK-NEXT: (local $4 i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.tee $3 + ;; CHECK-NEXT: (block $__asyncify_unwind + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const -4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $4 + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (try + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (call $fn1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch $default + ;; CHECK-NEXT: (local.set $2 + ;; CHECK-NEXT: (pop i32) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (try $foo-finally-1 + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (block $foo-pop-1 + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $fn2) + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (rethrow $foo-finally-1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (try $foo-finally-2 + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $fn3) + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (global.set $__asyncify_catch_counter + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (rethrow $foo-finally-2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $foo + (local $0 i32) + (try + (do + (call $fn1) + ) + (catch $default + (local.set $0 + (pop i32) + ) + (call $fn2) + ) + (catch_all + (call $fn3) + ) + ) + ) + + ;; CHECK: (func $fn1 + ;; CHECK-NEXT: ) + (func $fn1 + ) + + ;; CHECK: (func $fn2 + ;; CHECK-NEXT: ) + (func $fn2 + ) + + ;; CHECK: (func $fn3 + ;; CHECK-NEXT: ) + (func $fn3 + ) +) +;; CHECK: (func $asyncify_start_unwind (param $0 i32) +;; CHECK-NEXT: (if +;; CHECK-NEXT: (i32.ne +;; CHECK-NEXT: (global.get $__asyncify_catch_counter) +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (then +;; CHECK-NEXT: (return) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (global.set $__asyncify_state +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (global.set $__asyncify_data +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (if +;; CHECK-NEXT: (i32.gt_u +;; CHECK-NEXT: (i32.load +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (i32.load offset=4 +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (then +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $asyncify_stop_unwind +;; CHECK-NEXT: (global.set $__asyncify_state +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (if +;; CHECK-NEXT: (i32.gt_u +;; CHECK-NEXT: (i32.load +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (i32.load offset=4 +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (then +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $asyncify_start_rewind (param $0 i32) +;; CHECK-NEXT: (global.set $__asyncify_state +;; CHECK-NEXT: (i32.const 2) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (global.set $__asyncify_data +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (if +;; CHECK-NEXT: (i32.gt_u +;; CHECK-NEXT: (i32.load +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (i32.load offset=4 +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (then +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $asyncify_stop_rewind +;; CHECK-NEXT: (global.set $__asyncify_state +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (if +;; CHECK-NEXT: (i32.gt_u +;; CHECK-NEXT: (i32.load +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (i32.load offset=4 +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (then +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $asyncify_get_state (result i32) +;; CHECK-NEXT: (global.get $__asyncify_state) +;; CHECK-NEXT: ) + +;; CHECK: (func $asyncify_get_catch_counter (result i32) +;; CHECK-NEXT: (global.get $__asyncify_catch_counter) +;; CHECK-NEXT: ) diff --git a/test/lit/passes/asyncify_pass-arg=asyncify-eh.wast b/test/lit/passes/asyncify_pass-arg=asyncify-eh.wast new file mode 100644 index 00000000000..e3d932bb500 --- /dev/null +++ b/test/lit/passes/asyncify_pass-arg=asyncify-eh.wast @@ -0,0 +1,242 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. +;; NOTE: This test was ported using port_passes_tests_to_lit.py and could be cleaned up. + +;; RUN: foreach %s %t wasm-opt --enable-exception-handling --asyncify --pass-arg=asyncify-onlylist@foo -S -o - | filecheck %s + +(module + ;; CHECK: (type $0 (func)) + + ;; CHECK: (type $1 (func (param i32))) + + ;; CHECK: (type $2 (func (result i32))) + + ;; CHECK: (import "extmod" "exttag" (tag $default (type $1) (param i32))) + (import "extmod" "exttag" (tag $default (param i32))) + + (memory 1 2) + + ;; CHECK: (global $__asyncify_state (mut i32) (i32.const 0)) + + ;; CHECK: (global $__asyncify_data (mut i32) (i32.const 0)) + + ;; CHECK: (memory $0 1 2) + + ;; CHECK: (export "asyncify_start_unwind" (func $asyncify_start_unwind)) + + ;; CHECK: (export "asyncify_stop_unwind" (func $asyncify_stop_unwind)) + + ;; CHECK: (export "asyncify_start_rewind" (func $asyncify_start_rewind)) + + ;; CHECK: (export "asyncify_stop_rewind" (func $asyncify_stop_rewind)) + + ;; CHECK: (export "asyncify_get_state" (func $asyncify_get_state)) + + ;; CHECK: (func $foo + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (local $2 i32) + ;; CHECK-NEXT: (local $3 i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.tee $2 + ;; CHECK-NEXT: (block $__asyncify_unwind + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const -4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $3 + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (try + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (call $fn1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch $default + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (pop i32) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $fn2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (call $fn3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.store + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (global.get $__asyncify_data) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 4) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $foo + (local $0 i32) + (try + (do + (call $fn1) + ) + (catch $default + (local.set $0 + (pop i32) + ) + (call $fn2) + ) + (catch_all + (call $fn3) + ) + ) + ) + + ;; CHECK: (func $fn1 + ;; CHECK-NEXT: ) + (func $fn1 + ) + + ;; CHECK: (func $fn2 + ;; CHECK-NEXT: ) + (func $fn2 + ) + + ;; CHECK: (func $fn3 + ;; CHECK-NEXT: ) + (func $fn3 + ) +) +;; CHECK: (func $asyncify_start_unwind (param $0 i32) +;; CHECK-NEXT: (global.set $__asyncify_state +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (global.set $__asyncify_data +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (if +;; CHECK-NEXT: (i32.gt_u +;; CHECK-NEXT: (i32.load +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (i32.load offset=4 +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (then +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $asyncify_stop_unwind +;; CHECK-NEXT: (global.set $__asyncify_state +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (if +;; CHECK-NEXT: (i32.gt_u +;; CHECK-NEXT: (i32.load +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (i32.load offset=4 +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (then +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $asyncify_start_rewind (param $0 i32) +;; CHECK-NEXT: (global.set $__asyncify_state +;; CHECK-NEXT: (i32.const 2) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (global.set $__asyncify_data +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (if +;; CHECK-NEXT: (i32.gt_u +;; CHECK-NEXT: (i32.load +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (i32.load offset=4 +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (then +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $asyncify_stop_rewind +;; CHECK-NEXT: (global.set $__asyncify_state +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (if +;; CHECK-NEXT: (i32.gt_u +;; CHECK-NEXT: (i32.load +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (i32.load offset=4 +;; CHECK-NEXT: (global.get $__asyncify_data) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (then +;; CHECK-NEXT: (unreachable) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $asyncify_get_state (result i32) +;; CHECK-NEXT: (global.get $__asyncify_state) +;; CHECK-NEXT: ) From 5057cef022363acaf0bb306bfacc7d4bccfc6dcc Mon Sep 17 00:00:00 2001 From: Alexander Guryanov Date: Mon, 3 Nov 2025 22:17:12 +0300 Subject: [PATCH 02/15] Update src/passes/Asyncify.cpp Co-authored-by: Alon Zakai --- src/passes/Asyncify.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/passes/Asyncify.cpp b/src/passes/Asyncify.cpp index d200974a0f8..cfd45910bd6 100644 --- a/src/passes/Asyncify.cpp +++ b/src/passes/Asyncify.cpp @@ -96,12 +96,12 @@ // Overall, this should allow good performance with small overhead that is // mostly noticed at rewind time. // -// Exceptions handling (-fwasm-exceptions) is partially supported. Asyncify -// can't start unwind operation when a catch block is in the stack trace. -// If assertions mode is enabled then pass will check if unwind called from -// within catch block or not, and if so throw an unreachable exception. -// If "ignore unwind from catch" mode is enable then Asyncify will skip -// any unwind call from within catch block. +// Exceptions handling (-fwasm-exceptions) is partially supported, everything +// except for handling unwinding from within a catch block. If assertions mode +// is enabled then this pass will check for that problem, and if so, throw an +// unreachable exception. (If "ignore unwind from catch" mode is enabled then +// Asyncify will silently skip any unwind call from within catch blocks, see +// below.) // // After this pass is run a new i32 global "__asyncify_state" is added, which // has the following values: From a0e114bd7cc95547b5d60721f4c7f03db26adfa1 Mon Sep 17 00:00:00 2001 From: Alexander Guryanov Date: Mon, 3 Nov 2025 22:17:40 +0300 Subject: [PATCH 03/15] Update src/passes/Asyncify.cpp Co-authored-by: Alon Zakai --- src/passes/Asyncify.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/passes/Asyncify.cpp b/src/passes/Asyncify.cpp index cfd45910bd6..4a97eb43f2e 100644 --- a/src/passes/Asyncify.cpp +++ b/src/passes/Asyncify.cpp @@ -248,9 +248,9 @@ // // --pass-arg=asyncify-ignore-unwind-from-catch // -// This enables additional check to be performed before unwinding. In -// cases where the unwind operation is triggered from the catch block, -// it will be silently ignored (-fwasm-exceptions support) +// When an unwind operation is triggered from inside a wasm-exceptions +// catch block, which is not supported, silently ignore it rather than +// fail during rewinding later. // // --pass-arg=asyncify-verbose // From 372442efb4aca726ae362d107afa54ff99f5198a Mon Sep 17 00:00:00 2001 From: Alexander Guryanov Date: Mon, 3 Nov 2025 22:17:58 +0300 Subject: [PATCH 04/15] Update src/passes/Asyncify.cpp Co-authored-by: Alon Zakai --- src/passes/Asyncify.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/passes/Asyncify.cpp b/src/passes/Asyncify.cpp index 4a97eb43f2e..7bcd381d322 100644 --- a/src/passes/Asyncify.cpp +++ b/src/passes/Asyncify.cpp @@ -1248,7 +1248,7 @@ struct AsyncifyFlow : public Pass { } }; -// Add catch block counters to verify that unwind is not called from catch +// Add catch block counters to verify that unwinding is not done from catch // block. struct AsyncifyAddCatchCounters : public Pass { bool isFunctionParallel() override { return true; } From 1378904c1e3d510321ffb469103fad7d29ad89e6 Mon Sep 17 00:00:00 2001 From: Alexander Guryanov Date: Mon, 10 Nov 2025 15:25:59 +0300 Subject: [PATCH 05/15] Introduce AsyncifyAssertUnwindCorrectness --- src/passes/Asyncify.cpp | 323 ++++++++++------------------------------ 1 file changed, 81 insertions(+), 242 deletions(-) diff --git a/src/passes/Asyncify.cpp b/src/passes/Asyncify.cpp index 7bcd381d322..1d6d29dbeec 100644 --- a/src/passes/Asyncify.cpp +++ b/src/passes/Asyncify.cpp @@ -354,8 +354,6 @@ namespace { static const Name ASYNCIFY_STATE = "__asyncify_state"; static const Name ASYNCIFY_GET_STATE = "asyncify_get_state"; -static const Name ASYNCIFY_CATCH_COUNTER = "__asyncify_catch_counter"; -static const Name ASYNCIFY_GET_CATCH_COUNTER = "asyncify_get_catch_counter"; static const Name ASYNCIFY_DATA = "__asyncify_data"; static const Name ASYNCIFY_START_UNWIND = "asyncify_start_unwind"; static const Name ASYNCIFY_STOP_UNWIND = "asyncify_stop_unwind"; @@ -1248,202 +1246,6 @@ struct AsyncifyFlow : public Pass { } }; -// Add catch block counters to verify that unwinding is not done from catch -// block. -struct AsyncifyAddCatchCounters : public Pass { - bool isFunctionParallel() override { return true; } - - std::unique_ptr create() override { - return std::make_unique(); - } - - void runOnFunction(Module* module_, Function* func) override { - class CountersBuilder : public Builder { - public: - CountersBuilder(Module& wasm) : Builder(wasm) {} - Expression* makeInc(int amount = 1) { - return makeGlobalSet( - ASYNCIFY_CATCH_COUNTER, - makeBinary(AddInt32, - makeGlobalGet(ASYNCIFY_CATCH_COUNTER, Type::i32), - makeConst(int32_t(amount)))); - } - Expression* makeDec(int amount = 1) { - return makeGlobalSet( - ASYNCIFY_CATCH_COUNTER, - makeBinary(SubInt32, - makeGlobalGet(ASYNCIFY_CATCH_COUNTER, Type::i32), - makeConst(int32_t(amount)))); - } - }; - - // with this walker we will handle those changes of counter: - // - entering top-level catch (= pop) +1 - // - entering nested catch (= pop) 0 (ignored) - // - // - return inside top-level/nested catch -1 - // - return outside top-level/nested catch 0 (ignored) - // - // - break target outside of top-level catch -1 - // - break target inside of top-level catch 0 (ignored) - // - break outside top-level/nested catch 0 (ignored) - // - // - exiting from top-level catch -1 - // - exiting from nested catch 0 (ignored) - struct AddCountersWalker : public PostWalker { - Function* func; - CountersBuilder* builder; - BranchUtils::BranchTargets* branchTargets; - Parents* parents; - int finallyNum = 0; - int popNum = 0; - - int getCatchCount(Expression* expression) { - int catchCount = 0; - while (expression != func->body) { - auto parent = parents->getParent(expression); - if (auto* try_ = parent->dynCast()) { - if (try_->body != expression) { - catchCount++; - } - } - expression = parent; - } - - return catchCount; - } - - // Each catch block except catch_all should have pop instruction - // We increment counter each time when we enter top-level catch block - void visitPop(Pop* pop) { - if (getCatchCount(pop) == 1) { - auto name = - func->name.toString() + "-pop-" + std::to_string(++popNum); - replaceCurrent( - builder->makeBlock(name, {pop, builder->makeInc()}, Type::none)); - } - } - void visitLocalSet(LocalSet* set) { - auto block = set->value->dynCast(); // from visitPop above - if (block && block->name.hasSubstring("-pop-")) { - auto pop = block->list[0]->dynCast(); - assert(pop && getCatchCount(pop) == 1); - set->value = pop; - replaceCurrent(builder->makeBlock( - block->name, {set, builder->makeInc()}, Type::none)); - } - } - - // When return happens we decrement counter on 1, because we account - // only top-level catch blocks - // catch - // +1 - // catch - // ;; not counted - // -1 - // return - // ... - void visitReturn(Return* ret) { - if (getCatchCount(ret) > 0) { - replaceCurrent(builder->makeSequence(builder->makeDec(), ret)); - } - } - - // When break happens we decrement counter only if it goes out - // from top-level catch block - void visitBreak(Break* br) { - Expression* target = branchTargets->getTarget(br->name); - assert(target != nullptr); - if (getCatchCount(br) > 0 && getCatchCount(target) == 0) { - if (br->condition == nullptr) { - replaceCurrent(builder->makeSequence(builder->makeDec(), br)); - } else if (br->value == nullptr) { - auto decIf = - builder->makeIf(br->condition, - builder->makeSequence(builder->makeDec(), br), - nullptr); - br->condition = nullptr; - replaceCurrent(decIf); - } else { - Index newLocal = builder->addVar(func, br->value->type); - auto setLocal = builder->makeLocalSet(newLocal, br->value); - auto getLocal = builder->makeLocalGet(newLocal, br->value->type); - auto condition = br->condition; - br->condition = nullptr; - br->value = getLocal; - auto decIf = - builder->makeIf(condition, - builder->makeSequence(builder->makeDec(), br), - getLocal); - replaceCurrent(builder->makeSequence(setLocal, decIf)); - } - } - } - - // Replacing each top-level catch block with try/catch_all(finally) and - // increase counter for catch_all blocks (not handled by visitPop); dec - // counter at the end of catch block try ({fn}-finally-{label}) - // +1 - // {catch body} - // -1 - // catch_all - // -1 - // rethrow {fn}-finally-{label} - void visitTry(Try* curr) { - if (getCatchCount(curr) == 0) { - for (size_t i = 0; i < curr->catchBodies.size(); ++i) { - curr->catchBodies[i] = addCatchCounters( - curr->catchBodies[i], i == curr->catchTags.size()); - } - } - } - Expression* addCatchCounters(Expression* expression, bool catchAll) { - auto block = expression->dynCast(); - if (block == nullptr) { - block = builder->makeBlock(expression); - } - - // catch_all case is not covered by visitPop - if (catchAll) { - block->list.insertAt(0, builder->makeInc()); - } - - // dec counters at the end of catch - if (block->type == Type::none) { - auto last = block->list[block->list.size() - 1]; - if (!last->dynCast()) { - block->list.push_back(builder->makeDec()); - block->finalize(); - } - } - - auto name = - func->name.toString() + "-finally-" + std::to_string(++finallyNum); - return builder->makeTry( - name, - block, - {}, - {builder->makeSequence(builder->makeDec(), - builder->makeRethrow(name))}, - block->type); - } - }; - - Parents parents(func->body); - CountersBuilder builder(*module_); - BranchUtils::BranchTargets branchTargets(func->body); - - AddCountersWalker addCountersWalker; - addCountersWalker.func = func; - addCountersWalker.builder = &builder; - addCountersWalker.branchTargets = &branchTargets; - addCountersWalker.parents = &parents; - addCountersWalker.walk(func->body); - - EHUtils::handleBlockNestedPops(func, *module_); - } -}; - // Add asserts in non-instrumented code. struct AsyncifyAssertInNonInstrumented : public Pass { bool isFunctionParallel() override { return true; } @@ -1542,6 +1344,80 @@ struct AsyncifyAssertInNonInstrumented : public Pass { Module* module; }; +struct AsyncifyAssertUnwindCorrectness : Pass { + ModuleAnalyzer* analyzer; + Module* module; + + AsyncifyAssertUnwindCorrectness(ModuleAnalyzer* analyzer, Module* module) { + this->analyzer = analyzer; + this->module = module; + } + + std::unique_ptr create() override { + return std::make_unique(analyzer, module); + } + + bool isFunctionParallel() override { return true; } + + void runOnFunction(Module*, Function* function) override { + auto builder = std::make_unique(*module); + + struct UnwindWalker : WalkerPass> { + Function* function; + Builder* builder; + + void replaceCallWithCheck(Call* call) { + auto check = builder->makeIf( + builder->makeBinary(NeInt32, + builder->makeGlobalGet(ASYNCIFY_STATE, Type::i32), + builder->makeConst(int32_t(State::Normal))), + builder->makeUnreachable()); + if (call->type.isConcrete()) { + auto temp = builder->addVar(function, call->type); + replaceCurrent(builder->makeBlock( + { + builder->makeLocalSet(temp, call), + check, + builder->makeLocalGet(temp, call->type), + }, + call->type)); + } else { + replaceCurrent(builder->makeBlock( + { + call, + check, + }, + call->type)); + } + } + + void visitCall(Call* curr) { + assert(!expressionStack.empty()); + std::vector parents; + Index i = expressionStack.size() - 1; + while (i > 0) { + auto* expr = expressionStack[i]; + if (Try* aTry = expr->template dynCast()) { + // check if curr is inside body of aTry (which is safe), + // otherwise do replace a call + assert(i + 1 < expressionStack.size()); + if (expressionStack[i + 1] != aTry->body) { + replaceCallWithCheck(curr); + } + break; + } + i--; + } + }; + }; + + UnwindWalker walker; + walker.function = function; + walker.builder = builder.get(); + walker.walk(function->body); + } +}; + // Instrument local saving/restoring. struct AsyncifyLocals : public WalkerPass> { bool isFunctionParallel() override { return true; } @@ -1876,8 +1752,6 @@ struct Asyncify : public Pass { auto relocatable = hasArgument("asyncify-relocatable"); auto secondaryMemory = hasArgument("asyncify-in-secondary-memory"); auto propagateAddList = hasArgument("asyncify-propagate-addlist"); - auto ignoreCatchUnwind = hasArgument("asyncify-ignore-unwind-from-catch"); - auto addAsyncifyCounters = asserts || ignoreCatchUnwind; // Ensure there is a memory, as we need it. @@ -1942,7 +1816,7 @@ struct Asyncify : public Pass { verbose); // Add necessary globals before we emit code to use them. - addGlobals(module, relocatable, addAsyncifyCounters); + addGlobals(module, relocatable); // Compute the set of functions we will instrument. All of the passes we run // below only need to run there. @@ -1983,16 +1857,15 @@ struct Asyncify : public Pass { runner.setValidateGlobally(false); runner.run(); } - if (asserts || addAsyncifyCounters) { + if (asserts) { // Add asserts in non-instrumented code. Note we do not use an // instrumented pass runner here as we do want to run on all functions. PassRunner runner(module); - if (addAsyncifyCounters) { - runner.add(std::make_unique()); - } if (asserts) { runner.add(std::make_unique( &analyzer, pointerType, asyncifyMemory)); + runner.add( + std::make_unique(&analyzer, module)); } runner.setIsNested(true); runner.setValidateGlobally(false); @@ -2019,11 +1892,11 @@ struct Asyncify : public Pass { } // Finally, add function support (that should not have been seen by // the previous passes). - addFunctions(module, asserts, ignoreCatchUnwind); + addFunctions(module, asserts); } private: - void addGlobals(Module* module, bool imported, bool addAsyncifyCounters) { + void addGlobals(Module* module, bool imported) { Builder builder(*module); auto asyncifyState = builder.makeGlobal(ASYNCIFY_STATE, @@ -2036,19 +1909,6 @@ struct Asyncify : public Pass { } module->addGlobal(std::move(asyncifyState)); - if (addAsyncifyCounters) { - auto asyncifyCatchCounter = - builder.makeGlobal(ASYNCIFY_CATCH_COUNTER, - Type::i32, - builder.makeConst(int32_t(0)), - Builder::Mutable); - if (imported) { - asyncifyCatchCounter->module = ENV; - asyncifyCatchCounter->base = ASYNCIFY_CATCH_COUNTER; - } - module->addGlobal(std::move(asyncifyCatchCounter)); - } - auto asyncifyData = builder.makeGlobal(ASYNCIFY_DATA, pointerType, builder.makeConst(pointerType), @@ -2060,20 +1920,10 @@ struct Asyncify : public Pass { module->addGlobal(std::move(asyncifyData)); } - void addFunctions(Module* module, bool asserts, bool ignoreCatchUnwind) { + void addFunctions(Module* module, bool asserts) { Builder builder(*module); auto makeFunction = [&](Name name, bool setData, State state) { auto* body = builder.makeBlock(); - if (name == ASYNCIFY_START_UNWIND && (asserts || ignoreCatchUnwind)) { - auto* check = builder.makeIf( - builder.makeBinary( - NeInt32, - builder.makeGlobalGet(ASYNCIFY_CATCH_COUNTER, Type::i32), - builder.makeConst(int32_t(0))), - ignoreCatchUnwind ? (Expression*)builder.makeReturn() - : (Expression*)builder.makeUnreachable()); - body->list.push_back(check); - } std::vector params; if (setData) { params.push_back(pointerType); @@ -2125,17 +1975,6 @@ struct Asyncify : public Pass { builder.makeGlobalGet(ASYNCIFY_STATE, Type::i32))); module->addExport(builder.makeExport( ASYNCIFY_GET_STATE, ASYNCIFY_GET_STATE, ExternalKind::Function)); - - if (asserts || ignoreCatchUnwind) { - module->addFunction(builder.makeFunction( - ASYNCIFY_GET_CATCH_COUNTER, - Signature(Type::none, Type::i32), - {}, - builder.makeGlobalGet(ASYNCIFY_CATCH_COUNTER, Type::i32))); - module->addExport(builder.makeExport(ASYNCIFY_GET_CATCH_COUNTER, - ASYNCIFY_GET_CATCH_COUNTER, - ExternalKind::Function)); - } } Name createSecondaryMemory(Module* module, Address secondaryMemorySize) { From 3ca7b8ab578613717217ea89721680e86c4dd44f Mon Sep 17 00:00:00 2001 From: Alexander Guryanov Date: Mon, 10 Nov 2025 15:57:39 +0300 Subject: [PATCH 06/15] Update lit tests according changes --- ...serts_pass-arg=asyncify-onlylist@waka.wast | 21 +- ...asyncify_pass-arg=asyncify-eh-asserts.wast | 658 ++++++------------ .../asyncify_pass-arg=asyncify-eh-ignore.wast | 315 --------- 3 files changed, 234 insertions(+), 760 deletions(-) delete mode 100644 test/lit/passes/asyncify_pass-arg=asyncify-eh-ignore.wast diff --git a/test/lit/passes/asyncify_pass-arg=asyncify-asserts_pass-arg=asyncify-onlylist@waka.wast b/test/lit/passes/asyncify_pass-arg=asyncify-asserts_pass-arg=asyncify-onlylist@waka.wast index 1aa4462de01..d5dc9ff9bb4 100644 --- a/test/lit/passes/asyncify_pass-arg=asyncify-asserts_pass-arg=asyncify-onlylist@waka.wast +++ b/test/lit/passes/asyncify_pass-arg=asyncify-asserts_pass-arg=asyncify-onlylist@waka.wast @@ -11,9 +11,9 @@ (module ;; CHECK: (type $f (func)) (type $f (func)) - ;; CHECK: (type $1 (func (result i32))) + ;; CHECK: (type $1 (func (param i32))) - ;; CHECK: (type $2 (func (param i32))) + ;; CHECK: (type $2 (func (result i32))) ;; CHECK: (import "env" "import" (func $import)) (import "env" "import" (func $import)) @@ -27,8 +27,6 @@ (table funcref (elem $calls-import2-drop $calls-import2-drop)) ;; CHECK: (global $__asyncify_state (mut i32) (i32.const 0)) - ;; CHECK: (global $__asyncify_catch_counter (mut i32) (i32.const 0)) - ;; CHECK: (global $__asyncify_data (mut i32) (i32.const 0)) ;; CHECK: (memory $0 1 2) @@ -47,8 +45,6 @@ ;; CHECK: (export "asyncify_get_state" (func $asyncify_get_state)) - ;; CHECK: (export "asyncify_get_catch_counter" (func $asyncify_get_catch_counter)) - ;; CHECK: (func $calls-import ;; CHECK-NEXT: (local $0 i32) ;; CHECK-NEXT: (local.set $0 @@ -158,15 +154,6 @@ ) ;; CHECK: (func $asyncify_start_unwind (param $0 i32) -;; CHECK-NEXT: (if -;; CHECK-NEXT: (i32.ne -;; CHECK-NEXT: (global.get $__asyncify_catch_counter) -;; CHECK-NEXT: (i32.const 0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (then -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) ;; CHECK-NEXT: (global.set $__asyncify_state ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) @@ -251,7 +238,3 @@ ;; CHECK: (func $asyncify_get_state (result i32) ;; CHECK-NEXT: (global.get $__asyncify_state) ;; CHECK-NEXT: ) - -;; CHECK: (func $asyncify_get_catch_counter (result i32) -;; CHECK-NEXT: (global.get $__asyncify_catch_counter) -;; CHECK-NEXT: ) diff --git a/test/lit/passes/asyncify_pass-arg=asyncify-eh-asserts.wast b/test/lit/passes/asyncify_pass-arg=asyncify-eh-asserts.wast index 1bc520c4949..2a9a2b244ba 100644 --- a/test/lit/passes/asyncify_pass-arg=asyncify-eh-asserts.wast +++ b/test/lit/passes/asyncify_pass-arg=asyncify-eh-asserts.wast @@ -21,8 +21,6 @@ ;; CHECK: (global $__asyncify_state (mut i32) (i32.const 0)) - ;; CHECK: (global $__asyncify_catch_counter (mut i32) (i32.const 0)) - ;; CHECK: (global $__asyncify_data (mut i32) (i32.const 0)) ;; CHECK: (memory $0 1 2) @@ -37,8 +35,6 @@ ;; CHECK: (export "asyncify_get_state" (func $asyncify_get_state)) - ;; CHECK: (export "asyncify_get_catch_counter" (func $asyncify_get_catch_counter)) - ;; CHECK: (func $level0 ;; CHECK-NEXT: (local $0 i32) ;; CHECK-NEXT: (local $1 i32) @@ -54,9 +50,6 @@ ;; CHECK-NEXT: (local $11 i32) ;; CHECK-NEXT: (local $12 i32) ;; CHECK-NEXT: (local $13 i32) - ;; CHECK-NEXT: (local $14 i32) - ;; CHECK-NEXT: (local $15 i32) - ;; CHECK-NEXT: (local $16 i32) ;; CHECK-NEXT: (if ;; CHECK-NEXT: (i32.eq ;; CHECK-NEXT: (global.get $__asyncify_state) @@ -66,7 +59,7 @@ ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.tee $15 + ;; CHECK-NEXT: (local.tee $12 ;; CHECK-NEXT: (block $__asyncify_unwind ;; CHECK-NEXT: (block ;; CHECK-NEXT: (block @@ -85,7 +78,7 @@ ;; CHECK-NEXT: (i32.const -4) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $16 + ;; CHECK-NEXT: (local.set $13 ;; CHECK-NEXT: (i32.load ;; CHECK-NEXT: (i32.load ;; CHECK-NEXT: (global.get $__asyncify_data) @@ -133,191 +126,101 @@ ;; CHECK-NEXT: (call $fn1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch $default - ;; CHECK-NEXT: (local.set $12 + ;; CHECK-NEXT: (local.set $5 ;; CHECK-NEXT: (pop i32) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (try $level0-finally-1 - ;; CHECK-NEXT: (do - ;; CHECK-NEXT: (block $level0-pop-1 - ;; CHECK-NEXT: (local.set $5 - ;; CHECK-NEXT: (local.get $12) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (local.get $5) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $fn2) - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (local.get $5) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (call $fn2) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (catch_all - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (rethrow $level0-finally-1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch $ret - ;; CHECK-NEXT: (local.set $13 + ;; CHECK-NEXT: (local.set $6 ;; CHECK-NEXT: (pop i32) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (try $level0-finally-2 - ;; CHECK-NEXT: (do - ;; CHECK-NEXT: (block $level0-pop-2 - ;; CHECK-NEXT: (local.set $6 - ;; CHECK-NEXT: (local.get $13) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (local.get $6) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (local.set $7 - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $8 - ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (local.get $7) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (local.get $8) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (return) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (local.get $6) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (local.set $7 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $8 + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $7) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $fn2) - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $8) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (return) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (catch_all - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (call $fn2) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (rethrow $level0-finally-2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch $brif - ;; CHECK-NEXT: (local.set $14 + ;; CHECK-NEXT: (local.set $9 ;; CHECK-NEXT: (pop i32) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (try $level0-finally-3 - ;; CHECK-NEXT: (do - ;; CHECK-NEXT: (block $level0-pop-3 - ;; CHECK-NEXT: (local.set $9 - ;; CHECK-NEXT: (local.get $14) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (local.get $9) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $10 - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $11 - ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (local.get $10) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (local.get $11) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (br $label) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $fn2) - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (local.get $9) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $10 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $11 + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $10) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (catch_all - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br_if $label + ;; CHECK-NEXT: (local.get $11) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (call $fn2) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (rethrow $level0-finally-3) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all - ;; CHECK-NEXT: (try $level0-finally-4 - ;; CHECK-NEXT: (do - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $fn3) - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $fn3) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (catch_all - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (rethrow $level0-finally-4) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -335,7 +238,7 @@ ;; CHECK-NEXT: (i32.load ;; CHECK-NEXT: (global.get $__asyncify_data) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.get $15) + ;; CHECK-NEXT: (local.get $12) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (i32.store ;; CHECK-NEXT: (global.get $__asyncify_data) @@ -426,9 +329,6 @@ ;; CHECK-NEXT: (local $17 i32) ;; CHECK-NEXT: (local $18 i32) ;; CHECK-NEXT: (local $19 i32) - ;; CHECK-NEXT: (local $20 i32) - ;; CHECK-NEXT: (local $21 i32) - ;; CHECK-NEXT: (local $22 i32) ;; CHECK-NEXT: (if ;; CHECK-NEXT: (i32.eq ;; CHECK-NEXT: (global.get $__asyncify_state) @@ -438,7 +338,7 @@ ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.tee $21 + ;; CHECK-NEXT: (local.tee $18 ;; CHECK-NEXT: (block $__asyncify_unwind ;; CHECK-NEXT: (block ;; CHECK-NEXT: (block @@ -457,7 +357,7 @@ ;; CHECK-NEXT: (i32.const -4) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $22 + ;; CHECK-NEXT: (local.set $19 ;; CHECK-NEXT: (i32.load ;; CHECK-NEXT: (i32.load ;; CHECK-NEXT: (global.get $__asyncify_data) @@ -507,30 +407,14 @@ ;; CHECK-NEXT: (call $fn1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all - ;; CHECK-NEXT: (try $level1-finally-1 - ;; CHECK-NEXT: (do - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $fn2) - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $fn2) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (catch_all - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (rethrow $level1-finally-1) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -538,280 +422,215 @@ ;; CHECK-NEXT: (call $fn3) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch $default - ;; CHECK-NEXT: (local.set $18 + ;; CHECK-NEXT: (local.set $5 ;; CHECK-NEXT: (pop i32) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (try $level1-finally-2 + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (local.get $5) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (try ;; CHECK-NEXT: (do - ;; CHECK-NEXT: (block $level1-pop-1 - ;; CHECK-NEXT: (local.set $5 - ;; CHECK-NEXT: (local.get $18) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (local.get $5) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (try - ;; CHECK-NEXT: (do - ;; CHECK-NEXT: (call $fn1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (catch_all - ;; CHECK-NEXT: (call $fn3) + ;; CHECK-NEXT: (call $fn1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (call $fn3) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $fn2) - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (catch_all - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (call $fn2) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (rethrow $level1-finally-2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch $ret - ;; CHECK-NEXT: (local.set $19 + ;; CHECK-NEXT: (local.set $6 ;; CHECK-NEXT: (pop i32) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (try $level1-finally-3 + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (local.get $6) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (try ;; CHECK-NEXT: (do - ;; CHECK-NEXT: (block $level1-pop-2 - ;; CHECK-NEXT: (local.set $6 - ;; CHECK-NEXT: (local.get $19) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (local.get $6) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (try - ;; CHECK-NEXT: (do - ;; CHECK-NEXT: (call $fn1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (catch_all - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (local.set $7 - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $8 - ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (local.get $7) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (local.get $8) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (return) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $fn3) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $fn1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (local.set $9 + ;; CHECK-NEXT: (local.set $7 ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $10 + ;; CHECK-NEXT: (local.set $8 ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (local.get $9) + ;; CHECK-NEXT: (local.get $7) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (local.get $10) + ;; CHECK-NEXT: (local.get $8) ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (return) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (return) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $fn2) - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (call $fn3) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (catch_all - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (local.set $9 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $10 + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $9) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (local.get $10) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (call $fn2) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (rethrow $level1-finally-3) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch $brif - ;; CHECK-NEXT: (local.set $20 + ;; CHECK-NEXT: (local.set $11 ;; CHECK-NEXT: (pop i32) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (try $level1-finally-4 - ;; CHECK-NEXT: (do - ;; CHECK-NEXT: (block $level1-pop-3 - ;; CHECK-NEXT: (local.set $11 - ;; CHECK-NEXT: (local.get $20) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (local.get $11) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block $nottop + ;; CHECK-NEXT: (try + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (call $fn1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (local.set $12 + ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (local.set $13 + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $12) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (local.get $11) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block $nottop - ;; CHECK-NEXT: (try - ;; CHECK-NEXT: (do - ;; CHECK-NEXT: (call $fn1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (catch_all - ;; CHECK-NEXT: (local.set $12 - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $13 - ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (local.get $12) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (br_if $nottop - ;; CHECK-NEXT: (local.get $13) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $14 - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $15 - ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (local.get $14) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (local.get $15) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (br $label) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $fn3) + ;; CHECK-NEXT: (br_if $nottop + ;; CHECK-NEXT: (local.get $13) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $14 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $15 + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $14) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $16 - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $17 - ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (local.get $16) + ;; CHECK-NEXT: (br_if $label + ;; CHECK-NEXT: (local.get $15) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (local.get $17) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (call $fn3) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (br $label) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $fn2) - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (catch_all - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $16 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $17 + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $16) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br_if $label + ;; CHECK-NEXT: (local.get $17) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (call $fn2) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (rethrow $level1-finally-4) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (catch_all - ;; CHECK-NEXT: (try $level1-finally-5 + ;; CHECK-NEXT: (try ;; CHECK-NEXT: (do - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (try - ;; CHECK-NEXT: (do - ;; CHECK-NEXT: (call $fn1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (catch_all - ;; CHECK-NEXT: (call $fn2) + ;; CHECK-NEXT: (call $fn1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (call $fn2) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $fn3) - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (catch_all - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (call $fn3) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (global.get $__asyncify_state) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (rethrow $level1-finally-5) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -829,7 +648,7 @@ ;; CHECK-NEXT: (i32.load ;; CHECK-NEXT: (global.get $__asyncify_data) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.get $21) + ;; CHECK-NEXT: (local.get $18) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (i32.store ;; CHECK-NEXT: (global.get $__asyncify_data) @@ -992,15 +811,6 @@ ) ) ;; CHECK: (func $asyncify_start_unwind (param $0 i32) -;; CHECK-NEXT: (if -;; CHECK-NEXT: (i32.ne -;; CHECK-NEXT: (global.get $__asyncify_catch_counter) -;; CHECK-NEXT: (i32.const 0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (then -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) ;; CHECK-NEXT: (global.set $__asyncify_state ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) @@ -1085,7 +895,3 @@ ;; CHECK: (func $asyncify_get_state (result i32) ;; CHECK-NEXT: (global.get $__asyncify_state) ;; CHECK-NEXT: ) - -;; CHECK: (func $asyncify_get_catch_counter (result i32) -;; CHECK-NEXT: (global.get $__asyncify_catch_counter) -;; CHECK-NEXT: ) diff --git a/test/lit/passes/asyncify_pass-arg=asyncify-eh-ignore.wast b/test/lit/passes/asyncify_pass-arg=asyncify-eh-ignore.wast deleted file mode 100644 index 092c3b830a6..00000000000 --- a/test/lit/passes/asyncify_pass-arg=asyncify-eh-ignore.wast +++ /dev/null @@ -1,315 +0,0 @@ -;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -;; NOTE: This test was ported using port_passes_tests_to_lit.py and could be cleaned up. - -;; RUN: foreach %s %t wasm-opt --enable-exception-handling --asyncify --pass-arg=asyncify-ignore-unwind-from-catch --pass-arg=asyncify-onlylist@foo -S -o - | filecheck %s - -(module - ;; CHECK: (type $0 (func)) - - ;; CHECK: (type $1 (func (param i32))) - - ;; CHECK: (type $2 (func (result i32))) - - ;; CHECK: (import "extmod" "exttag" (tag $default (type $1) (param i32))) - (import "extmod" "exttag" (tag $default (param i32))) - - (memory 1 2) - - ;; CHECK: (global $__asyncify_state (mut i32) (i32.const 0)) - - ;; CHECK: (global $__asyncify_catch_counter (mut i32) (i32.const 0)) - - ;; CHECK: (global $__asyncify_data (mut i32) (i32.const 0)) - - ;; CHECK: (memory $0 1 2) - - ;; CHECK: (export "asyncify_start_unwind" (func $asyncify_start_unwind)) - - ;; CHECK: (export "asyncify_stop_unwind" (func $asyncify_stop_unwind)) - - ;; CHECK: (export "asyncify_start_rewind" (func $asyncify_start_rewind)) - - ;; CHECK: (export "asyncify_stop_rewind" (func $asyncify_stop_rewind)) - - ;; CHECK: (export "asyncify_get_state" (func $asyncify_get_state)) - - ;; CHECK: (export "asyncify_get_catch_counter" (func $asyncify_get_catch_counter)) - - ;; CHECK: (func $foo - ;; CHECK-NEXT: (local $0 i32) - ;; CHECK-NEXT: (local $1 i32) - ;; CHECK-NEXT: (local $2 i32) - ;; CHECK-NEXT: (local $3 i32) - ;; CHECK-NEXT: (local $4 i32) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.eq - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (nop) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.tee $3 - ;; CHECK-NEXT: (block $__asyncify_unwind - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.eq - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (i32.store - ;; CHECK-NEXT: (global.get $__asyncify_data) - ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (global.get $__asyncify_data) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const -4) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $4 - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (global.get $__asyncify_data) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.eq - ;; CHECK-NEXT: (global.get $__asyncify_state) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (try - ;; CHECK-NEXT: (do - ;; CHECK-NEXT: (call $fn1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (catch $default - ;; CHECK-NEXT: (local.set $2 - ;; CHECK-NEXT: (pop i32) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (try $foo-finally-1 - ;; CHECK-NEXT: (do - ;; CHECK-NEXT: (block $foo-pop-1 - ;; CHECK-NEXT: (local.set $1 - ;; CHECK-NEXT: (local.get $2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $0 - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $fn2) - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (catch_all - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (rethrow $foo-finally-1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (catch_all - ;; CHECK-NEXT: (try $foo-finally-2 - ;; CHECK-NEXT: (do - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (call $fn3) - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (catch_all - ;; CHECK-NEXT: (global.set $__asyncify_catch_counter - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (global.get $__asyncify_catch_counter) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (rethrow $foo-finally-2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (return) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (block - ;; CHECK-NEXT: (i32.store - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (global.get $__asyncify_data) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.get $3) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.store - ;; CHECK-NEXT: (global.get $__asyncify_data) - ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (global.get $__asyncify_data) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 4) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (nop) - ;; CHECK-NEXT: ) - (func $foo - (local $0 i32) - (try - (do - (call $fn1) - ) - (catch $default - (local.set $0 - (pop i32) - ) - (call $fn2) - ) - (catch_all - (call $fn3) - ) - ) - ) - - ;; CHECK: (func $fn1 - ;; CHECK-NEXT: ) - (func $fn1 - ) - - ;; CHECK: (func $fn2 - ;; CHECK-NEXT: ) - (func $fn2 - ) - - ;; CHECK: (func $fn3 - ;; CHECK-NEXT: ) - (func $fn3 - ) -) -;; CHECK: (func $asyncify_start_unwind (param $0 i32) -;; CHECK-NEXT: (if -;; CHECK-NEXT: (i32.ne -;; CHECK-NEXT: (global.get $__asyncify_catch_counter) -;; CHECK-NEXT: (i32.const 0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (then -;; CHECK-NEXT: (return) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (global.set $__asyncify_state -;; CHECK-NEXT: (i32.const 1) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (global.set $__asyncify_data -;; CHECK-NEXT: (local.get $0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (if -;; CHECK-NEXT: (i32.gt_u -;; CHECK-NEXT: (i32.load -;; CHECK-NEXT: (global.get $__asyncify_data) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (i32.load offset=4 -;; CHECK-NEXT: (global.get $__asyncify_data) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (then -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $asyncify_stop_unwind -;; CHECK-NEXT: (global.set $__asyncify_state -;; CHECK-NEXT: (i32.const 0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (if -;; CHECK-NEXT: (i32.gt_u -;; CHECK-NEXT: (i32.load -;; CHECK-NEXT: (global.get $__asyncify_data) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (i32.load offset=4 -;; CHECK-NEXT: (global.get $__asyncify_data) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (then -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $asyncify_start_rewind (param $0 i32) -;; CHECK-NEXT: (global.set $__asyncify_state -;; CHECK-NEXT: (i32.const 2) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (global.set $__asyncify_data -;; CHECK-NEXT: (local.get $0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (if -;; CHECK-NEXT: (i32.gt_u -;; CHECK-NEXT: (i32.load -;; CHECK-NEXT: (global.get $__asyncify_data) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (i32.load offset=4 -;; CHECK-NEXT: (global.get $__asyncify_data) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (then -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $asyncify_stop_rewind -;; CHECK-NEXT: (global.set $__asyncify_state -;; CHECK-NEXT: (i32.const 0) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (if -;; CHECK-NEXT: (i32.gt_u -;; CHECK-NEXT: (i32.load -;; CHECK-NEXT: (global.get $__asyncify_data) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (i32.load offset=4 -;; CHECK-NEXT: (global.get $__asyncify_data) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: (then -;; CHECK-NEXT: (unreachable) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) -;; CHECK-NEXT: ) - -;; CHECK: (func $asyncify_get_state (result i32) -;; CHECK-NEXT: (global.get $__asyncify_state) -;; CHECK-NEXT: ) - -;; CHECK: (func $asyncify_get_catch_counter (result i32) -;; CHECK-NEXT: (global.get $__asyncify_catch_counter) -;; CHECK-NEXT: ) From b60b75d527566ac979a5acb64b9b7f42f42a5652 Mon Sep 17 00:00:00 2001 From: Alexander Guryanov Date: Tue, 11 Nov 2025 10:18:36 +0300 Subject: [PATCH 07/15] Update src/passes/Asyncify.cpp Co-authored-by: Alon Zakai --- src/passes/Asyncify.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/passes/Asyncify.cpp b/src/passes/Asyncify.cpp index 1d6d29dbeec..f69176ada50 100644 --- a/src/passes/Asyncify.cpp +++ b/src/passes/Asyncify.cpp @@ -250,7 +250,7 @@ // // When an unwind operation is triggered from inside a wasm-exceptions // catch block, which is not supported, silently ignore it rather than -// fail during rewinding later. +// fail during rewinding later. (This is unsafe in general.) // // --pass-arg=asyncify-verbose // From fc47f685950c56f6904f58f94d7b6bb7884c3fa9 Mon Sep 17 00:00:00 2001 From: Alexander Guryanov Date: Tue, 11 Nov 2025 10:18:45 +0300 Subject: [PATCH 08/15] Update src/passes/Asyncify.cpp Co-authored-by: Alon Zakai --- src/passes/Asyncify.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/passes/Asyncify.cpp b/src/passes/Asyncify.cpp index f69176ada50..d17da25b0fd 100644 --- a/src/passes/Asyncify.cpp +++ b/src/passes/Asyncify.cpp @@ -339,7 +339,6 @@ #include "ir/memory-utils.h" #include "ir/module-utils.h" #include "ir/names.h" -#include "ir/parents.h" #include "ir/utils.h" #include "pass.h" #include "passes/pass-utils.h" From 8d376052e552a44d5c8063776322b9aa5f7ff2ed Mon Sep 17 00:00:00 2001 From: Alexander Guryanov Date: Tue, 11 Nov 2025 10:18:54 +0300 Subject: [PATCH 09/15] Update src/passes/Asyncify.cpp Co-authored-by: Alon Zakai --- src/passes/Asyncify.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/passes/Asyncify.cpp b/src/passes/Asyncify.cpp index d17da25b0fd..ba9f6e71cf7 100644 --- a/src/passes/Asyncify.cpp +++ b/src/passes/Asyncify.cpp @@ -330,7 +330,6 @@ #include "asmjs/shared-constants.h" #include "cfg/liveness-traversal.h" -#include "ir/branch-utils.h" #include "ir/effects.h" #include "ir/eh-utils.h" #include "ir/find_all.h" From 4175a77304dd1221b177cf8eb8116f99288329fe Mon Sep 17 00:00:00 2001 From: Alexander Guryanov Date: Tue, 11 Nov 2025 10:19:12 +0300 Subject: [PATCH 10/15] Update src/passes/Asyncify.cpp Co-authored-by: Alon Zakai --- src/passes/Asyncify.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/passes/Asyncify.cpp b/src/passes/Asyncify.cpp index ba9f6e71cf7..3d046a14bd8 100644 --- a/src/passes/Asyncify.cpp +++ b/src/passes/Asyncify.cpp @@ -1364,7 +1364,8 @@ struct AsyncifyAssertUnwindCorrectness : Pass { Function* function; Builder* builder; - void replaceCallWithCheck(Call* call) { + // Adds a check for Call that is inside a Catch block (we do not handle unwinding there). + void checkCallInsideCatch(Call* call) { auto check = builder->makeIf( builder->makeBinary(NeInt32, builder->makeGlobalGet(ASYNCIFY_STATE, Type::i32), From 7fd957968e57839b49f4286582e501475a34f201 Mon Sep 17 00:00:00 2001 From: Alexander Guryanov Date: Tue, 11 Nov 2025 10:19:30 +0300 Subject: [PATCH 11/15] Update src/passes/Asyncify.cpp Co-authored-by: Alon Zakai --- src/passes/Asyncify.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/passes/Asyncify.cpp b/src/passes/Asyncify.cpp index 3d046a14bd8..7b7ccbfa21f 100644 --- a/src/passes/Asyncify.cpp +++ b/src/passes/Asyncify.cpp @@ -1392,7 +1392,6 @@ struct AsyncifyAssertUnwindCorrectness : Pass { void visitCall(Call* curr) { assert(!expressionStack.empty()); - std::vector parents; Index i = expressionStack.size() - 1; while (i > 0) { auto* expr = expressionStack[i]; From af58be1a51a3ea60e937b8cd5c3d8373c236cc25 Mon Sep 17 00:00:00 2001 From: Alexander Guryanov Date: Tue, 11 Nov 2025 10:22:10 +0300 Subject: [PATCH 12/15] Update src/passes/Asyncify.cpp Co-authored-by: Alon Zakai --- src/passes/Asyncify.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/passes/Asyncify.cpp b/src/passes/Asyncify.cpp index 7b7ccbfa21f..2ac32d23f4d 100644 --- a/src/passes/Asyncify.cpp +++ b/src/passes/Asyncify.cpp @@ -331,7 +331,6 @@ #include "asmjs/shared-constants.h" #include "cfg/liveness-traversal.h" #include "ir/effects.h" -#include "ir/eh-utils.h" #include "ir/find_all.h" #include "ir/linear-execution.h" #include "ir/literal-utils.h" From e9b062f89d2365cd1432ec84d13a97cf5cb0185c Mon Sep 17 00:00:00 2001 From: Alexander Guryanov Date: Tue, 11 Nov 2025 10:22:26 +0300 Subject: [PATCH 13/15] Update src/passes/Asyncify.cpp Co-authored-by: Alon Zakai --- src/passes/Asyncify.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/passes/Asyncify.cpp b/src/passes/Asyncify.cpp index 2ac32d23f4d..9e3d97a0339 100644 --- a/src/passes/Asyncify.cpp +++ b/src/passes/Asyncify.cpp @@ -1391,6 +1391,7 @@ struct AsyncifyAssertUnwindCorrectness : Pass { void visitCall(Call* curr) { assert(!expressionStack.empty()); + // Go up the stack and see if we are in a Catch. Index i = expressionStack.size() - 1; while (i > 0) { auto* expr = expressionStack[i]; From 378138e949525b9f3229870be79512efcf20a62d Mon Sep 17 00:00:00 2001 From: Alexander Guryanov Date: Wed, 19 Nov 2025 13:49:37 +0300 Subject: [PATCH 14/15] wip --- src/passes/Asyncify.cpp | 64 +++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/src/passes/Asyncify.cpp b/src/passes/Asyncify.cpp index 9e3d97a0339..38d43e23474 100644 --- a/src/passes/Asyncify.cpp +++ b/src/passes/Asyncify.cpp @@ -1342,6 +1342,8 @@ struct AsyncifyAssertInNonInstrumented : public Pass { }; struct AsyncifyAssertUnwindCorrectness : Pass { + bool isFunctionParallel() override { return true; } + ModuleAnalyzer* analyzer; Module* module; @@ -1354,17 +1356,14 @@ struct AsyncifyAssertUnwindCorrectness : Pass { return std::make_unique(analyzer, module); } - bool isFunctionParallel() override { return true; } - - void runOnFunction(Module*, Function* function) override { - auto builder = std::make_unique(*module); - + void runOnFunction(Module* module_, Function* function) override { struct UnwindWalker : WalkerPass> { Function* function; - Builder* builder; + Module* module; // Adds a check for Call that is inside a Catch block (we do not handle unwinding there). - void checkCallInsideCatch(Call* call) { + void replaceCallWithCheck(Call* call) { + auto builder = std::make_unique(*module); auto check = builder->makeIf( builder->makeBinary(NeInt32, builder->makeGlobalGet(ASYNCIFY_STATE, Type::i32), @@ -1391,6 +1390,11 @@ struct AsyncifyAssertUnwindCorrectness : Pass { void visitCall(Call* curr) { assert(!expressionStack.empty()); + // A return_call (curr->isReturn) can be ignored here: It returns first, + // leaving the Catch, before calling. + if (curr->isReturn) { + return; + } // Go up the stack and see if we are in a Catch. Index i = expressionStack.size() - 1; while (i > 0) { @@ -1411,7 +1415,7 @@ struct AsyncifyAssertUnwindCorrectness : Pass { UnwindWalker walker; walker.function = function; - walker.builder = builder.get(); + walker.module = module_; walker.walk(function->body); } }; @@ -1855,7 +1859,6 @@ struct Asyncify : public Pass { runner.setValidateGlobally(false); runner.run(); } - if (asserts) { // Add asserts in non-instrumented code. Note we do not use an // instrumented pass runner here as we do want to run on all functions. PassRunner runner(module); @@ -1868,29 +1871,28 @@ struct Asyncify : public Pass { runner.setIsNested(true); runner.setValidateGlobally(false); runner.run(); - } - // Next, add local saving/restoring logic. We optimize before doing this, - // to undo the extra code generated by flattening, and to arrive at the - // minimal amount of locals (which is important as we must save and - // restore those locals). We also and optimize after as well to simplify - // the code as much as possible. - { - PassUtils::FilteredPassRunner runner(module, instrumentedFuncs); - if (optimize) { - runner.addDefaultFunctionOptimizationPasses(); - } - runner.add(std::make_unique( - &analyzer, pointerType, asyncifyMemory)); - if (optimize) { - runner.addDefaultFunctionOptimizationPasses(); + // Next, add local saving/restoring logic. We optimize before doing this, + // to undo the extra code generated by flattening, and to arrive at the + // minimal amount of locals (which is important as we must save and + // restore those locals). We also and optimize after as well to simplify + // the code as much as possible. + { + PassUtils::FilteredPassRunner runner(module, instrumentedFuncs); + if (optimize) { + runner.addDefaultFunctionOptimizationPasses(); + } + runner.add(std::make_unique( + &analyzer, pointerType, asyncifyMemory)); + if (optimize) { + runner.addDefaultFunctionOptimizationPasses(); + } + runner.setIsNested(true); + runner.setValidateGlobally(false); + runner.run(); } - runner.setIsNested(true); - runner.setValidateGlobally(false); - runner.run(); - } // Finally, add function support (that should not have been seen by // the previous passes). - addFunctions(module, asserts); + addFunctions(module); } private: @@ -1918,14 +1920,14 @@ struct Asyncify : public Pass { module->addGlobal(std::move(asyncifyData)); } - void addFunctions(Module* module, bool asserts) { + void addFunctions(Module* module) { Builder builder(*module); auto makeFunction = [&](Name name, bool setData, State state) { - auto* body = builder.makeBlock(); std::vector params; if (setData) { params.push_back(pointerType); } + auto* body = builder.makeBlock(); body->list.push_back(builder.makeGlobalSet( ASYNCIFY_STATE, builder.makeConst(int32_t(state)))); if (setData) { From 39dda813f921e281da8ca64e66ee44a9d7f07e10 Mon Sep 17 00:00:00 2001 From: Alexander Guryanov Date: Wed, 19 Nov 2025 13:57:59 +0300 Subject: [PATCH 15/15] introduce visitCallLike to handle callRef, callIndirect --- src/passes/Asyncify.cpp | 172 +++++++++++++++++++++------------------- 1 file changed, 90 insertions(+), 82 deletions(-) diff --git a/src/passes/Asyncify.cpp b/src/passes/Asyncify.cpp index 38d43e23474..cadf0871b4d 100644 --- a/src/passes/Asyncify.cpp +++ b/src/passes/Asyncify.cpp @@ -1341,6 +1341,70 @@ struct AsyncifyAssertInNonInstrumented : public Pass { Module* module; }; +struct AsyncifyUnwindWalker + : WalkerPass> { + Function* function; + Module* module; + + // Adds a check for Call that is inside a Catch block (we do not handle + // unwinding there). + template void replaceCallWithCheck(T* call) { + auto builder = std::make_unique(*module); + auto check = builder->makeIf( + builder->makeBinary(NeInt32, + builder->makeGlobalGet(ASYNCIFY_STATE, Type::i32), + builder->makeConst(int32_t(State::Normal))), + builder->makeUnreachable()); + if (call->type.isConcrete()) { + auto temp = builder->addVar(function, call->type); + replaceCurrent(builder->makeBlock( + { + builder->makeLocalSet(temp, call), + check, + builder->makeLocalGet(temp, call->type), + }, + call->type)); + } else { + replaceCurrent(builder->makeBlock( + { + call, + check, + }, + call->type)); + } + } + + template void visitCallLike(T* curr) { + assert(!expressionStack.empty()); + // A return_call (curr->isReturn) can be ignored here: It returns first, + // leaving the Catch, before calling. + if (curr->isReturn) { + return; + } + // Go up the stack and see if we are in a Catch. + Index i = expressionStack.size() - 1; + while (i > 0) { + auto* expr = expressionStack[i]; + if (Try* aTry = expr->template dynCast()) { + // check if curr is inside body of aTry (which is safe), + // otherwise do replace a call + assert(i + 1 < expressionStack.size()); + if (expressionStack[i + 1] != aTry->body) { + replaceCallWithCheck(curr); + } + break; + } + i--; + } + } + + void visitCall(Call* curr) { visitCallLike(curr); } + + void visitCallRef(CallRef* curr) { visitCallLike(curr); } + + void visitCallIndirect(CallIndirect* curr) { visitCallLike(curr); } +}; + struct AsyncifyAssertUnwindCorrectness : Pass { bool isFunctionParallel() override { return true; } @@ -1357,63 +1421,7 @@ struct AsyncifyAssertUnwindCorrectness : Pass { } void runOnFunction(Module* module_, Function* function) override { - struct UnwindWalker : WalkerPass> { - Function* function; - Module* module; - - // Adds a check for Call that is inside a Catch block (we do not handle unwinding there). - void replaceCallWithCheck(Call* call) { - auto builder = std::make_unique(*module); - auto check = builder->makeIf( - builder->makeBinary(NeInt32, - builder->makeGlobalGet(ASYNCIFY_STATE, Type::i32), - builder->makeConst(int32_t(State::Normal))), - builder->makeUnreachable()); - if (call->type.isConcrete()) { - auto temp = builder->addVar(function, call->type); - replaceCurrent(builder->makeBlock( - { - builder->makeLocalSet(temp, call), - check, - builder->makeLocalGet(temp, call->type), - }, - call->type)); - } else { - replaceCurrent(builder->makeBlock( - { - call, - check, - }, - call->type)); - } - } - - void visitCall(Call* curr) { - assert(!expressionStack.empty()); - // A return_call (curr->isReturn) can be ignored here: It returns first, - // leaving the Catch, before calling. - if (curr->isReturn) { - return; - } - // Go up the stack and see if we are in a Catch. - Index i = expressionStack.size() - 1; - while (i > 0) { - auto* expr = expressionStack[i]; - if (Try* aTry = expr->template dynCast()) { - // check if curr is inside body of aTry (which is safe), - // otherwise do replace a call - assert(i + 1 < expressionStack.size()); - if (expressionStack[i + 1] != aTry->body) { - replaceCallWithCheck(curr); - } - break; - } - i--; - } - }; - }; - - UnwindWalker walker; + AsyncifyUnwindWalker walker; walker.function = function; walker.module = module_; walker.walk(function->body); @@ -1859,40 +1867,40 @@ struct Asyncify : public Pass { runner.setValidateGlobally(false); runner.run(); } + if (asserts) { // Add asserts in non-instrumented code. Note we do not use an // instrumented pass runner here as we do want to run on all functions. PassRunner runner(module); - if (asserts) { - runner.add(std::make_unique( - &analyzer, pointerType, asyncifyMemory)); - runner.add( - std::make_unique(&analyzer, module)); - } + runner.add(std::make_unique( + &analyzer, pointerType, asyncifyMemory)); + runner.add( + std::make_unique(&analyzer, module)); runner.setIsNested(true); runner.setValidateGlobally(false); runner.run(); - // Next, add local saving/restoring logic. We optimize before doing this, - // to undo the extra code generated by flattening, and to arrive at the - // minimal amount of locals (which is important as we must save and - // restore those locals). We also and optimize after as well to simplify - // the code as much as possible. - { - PassUtils::FilteredPassRunner runner(module, instrumentedFuncs); - if (optimize) { - runner.addDefaultFunctionOptimizationPasses(); - } - runner.add(std::make_unique( - &analyzer, pointerType, asyncifyMemory)); - if (optimize) { - runner.addDefaultFunctionOptimizationPasses(); - } - runner.setIsNested(true); - runner.setValidateGlobally(false); - runner.run(); + } + // Next, add local saving/restoring logic. We optimize before doing this, + // to undo the extra code generated by flattening, and to arrive at the + // minimal amount of locals (which is important as we must save and + // restore those locals). We also and optimize after as well to simplify + // the code as much as possible. + { + PassUtils::FilteredPassRunner runner(module, instrumentedFuncs); + if (optimize) { + runner.addDefaultFunctionOptimizationPasses(); } + runner.add(std::make_unique( + &analyzer, pointerType, asyncifyMemory)); + if (optimize) { + runner.addDefaultFunctionOptimizationPasses(); + } + runner.setIsNested(true); + runner.setValidateGlobally(false); + runner.run(); + } // Finally, add function support (that should not have been seen by // the previous passes). - addFunctions(module); + addFunctions(module); } private: