summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/passes/Asyncify.cpp146
-rw-r--r--src/passes/pass.cpp7
-rw-r--r--src/passes/passes.h2
-rw-r--r--test/passes/asyncify_mod-asyncify-always-and-only-unwind.txt516
-rw-r--r--test/passes/asyncify_mod-asyncify-always-and-only-unwind.wast20
-rw-r--r--test/passes/asyncify_mod-asyncify-always-and-only-unwind_O.txt88
-rw-r--r--test/passes/asyncify_mod-asyncify-always-and-only-unwind_O.wast24
-rw-r--r--test/passes/asyncify_mod-asyncify-never-unwind.txt534
-rw-r--r--test/passes/asyncify_mod-asyncify-never-unwind.wast20
-rw-r--r--test/passes/asyncify_mod-asyncify-never-unwind_O.txt103
-rw-r--r--test/passes/asyncify_mod-asyncify-never-unwind_O.wast24
11 files changed, 1483 insertions, 1 deletions
diff --git a/src/passes/Asyncify.cpp b/src/passes/Asyncify.cpp
index 56c294b21..c4e1c58bd 100644
--- a/src/passes/Asyncify.cpp
+++ b/src/passes/Asyncify.cpp
@@ -253,6 +253,7 @@
//
#include "ir/effects.h"
+#include "ir/find_all.h"
#include "ir/literal-utils.h"
#include "ir/memory-utils.h"
#include "ir/module-utils.h"
@@ -1169,6 +1170,10 @@ private:
} // anonymous namespace
+static std::string getFullImportName(Name module, Name base) {
+ return std::string(module.str) + '.' + base.str;
+}
+
struct Asyncify : public Pass {
void run(PassRunner* runner, Module* module) override {
bool optimize = runner->options.optimizeLevel > 0;
@@ -1209,7 +1214,7 @@ struct Asyncify : public Pass {
if (allImportsCanChangeState) {
return true;
}
- std::string full = std::string(module.str) + '.' + base.str;
+ auto full = getFullImportName(module, base);
for (auto& listedImport : listedImports) {
if (String::wildcardMatch(listedImport, full)) {
return true;
@@ -1341,4 +1346,143 @@ private:
Pass* createAsyncifyPass() { return new Asyncify(); }
+// Helper passes that can be run after Asyncify.
+
+template<bool neverRewind, bool neverUnwind, bool importsAlwaysUnwind>
+struct ModAsyncify
+ : public WalkerPass<LinearExecutionWalker<
+ ModAsyncify<neverRewind, neverUnwind, importsAlwaysUnwind>>> {
+ bool isFunctionParallel() override { return true; }
+
+ ModAsyncify* create() override {
+ return new ModAsyncify<neverRewind, neverUnwind, importsAlwaysUnwind>();
+ }
+
+ void doWalkFunction(Function* func) {
+ // Find the asyncify state name.
+ auto* unwind = this->getModule()->getExport(ASYNCIFY_STOP_UNWIND);
+ auto* unwindFunc = this->getModule()->getFunction(unwind->value);
+ FindAll<GlobalSet> sets(unwindFunc->body);
+ assert(sets.list.size() == 1);
+ asyncifyStateName = sets.list[0]->name;
+ // Walk and optimize.
+ this->walk(func->body);
+ }
+
+ // Note that we don't just implement GetGlobal as we may know the value is
+ // *not* 0, 1, or 2, but not know the actual value. So what we can say depends
+ // on the comparison being done on it, and so we implement Binary and
+ // Select.
+
+ void visitBinary(Binary* curr) {
+ // Check if this is a comparison of the asyncify state to a specific
+ // constant, which we may know is impossible.
+ bool flip = false;
+ if (curr->op == NeInt32) {
+ flip = true;
+ } else if (curr->op != EqInt32) {
+ return;
+ }
+ auto* c = curr->right->dynCast<Const>();
+ if (!c) {
+ return;
+ }
+ auto* get = curr->left->dynCast<GlobalGet>();
+ if (!get || get->name != asyncifyStateName) {
+ return;
+ }
+ // This is a comparison of the state to a constant, check if we know the
+ // value.
+ int32_t value;
+ auto checkedValue = c->value.geti32();
+ if ((checkedValue == int(State::Unwinding) && neverUnwind) ||
+ (checkedValue == int(State::Rewinding) && neverRewind)) {
+ // We know the state is checked against an impossible value.
+ value = 0;
+ } else if (checkedValue == int(State::Unwinding) && this->unwinding) {
+ // We know we are in fact unwinding right now.
+ value = 1;
+ unsetUnwinding();
+ } else {
+ return;
+ }
+ if (flip) {
+ value = 1 - value;
+ }
+ Builder builder(*this->getModule());
+ this->replaceCurrent(builder.makeConst(Literal(int32_t(value))));
+ }
+
+ void visitSelect(Select* curr) {
+ auto* get = curr->condition->dynCast<GlobalGet>();
+ if (!get || get->name != asyncifyStateName) {
+ return;
+ }
+ // This is a comparison of the state to zero, which means we are checking
+ // "if running normally, run this code, but if rewinding, ignore it". If
+ // we know we'll never rewind, we can optimize this.
+ if (neverRewind) {
+ Builder builder(*this->getModule());
+ curr->condition = builder.makeConst(Literal(int32_t(0)));
+ }
+ }
+
+ void visitCall(Call* curr) {
+ unsetUnwinding();
+ if (!importsAlwaysUnwind) {
+ return;
+ }
+ auto* target = this->getModule()->getFunction(curr->target);
+ if (!target->imported()) {
+ return;
+ }
+ // This is an import that definitely unwinds. Await the next check of
+ // the state in this linear execution trace, which we can turn into a
+ // constant.
+ this->unwinding = true;
+ }
+
+ void visitCallIndirect(CallIndirect* curr) { unsetUnwinding(); }
+
+ static void doNoteNonLinear(
+ ModAsyncify<neverRewind, neverUnwind, importsAlwaysUnwind>* self,
+ Expression**) {
+ // When control flow branches, stop tracking an unwinding.
+ self->unsetUnwinding();
+ }
+
+ void visitGlobalSet(GlobalSet* set) {
+ // TODO: this could be more precise
+ unsetUnwinding();
+ }
+
+private:
+ Name asyncifyStateName;
+
+ // Whether we just did a call to an import that indicates we are unwinding.
+ bool unwinding = false;
+
+ void unsetUnwinding() { this->unwinding = false; }
+};
+
+//
+// Assume imports that may unwind will always unwind, and that rewinding never
+// happens.
+//
+
+Pass* createModAsyncifyAlwaysOnlyUnwindPass() {
+ return new ModAsyncify<true, false, true>();
+}
+
+//
+// Assume that we never unwind, but may still rewind.
+//
+struct ModAsyncifyNeverUnwind : public Pass {
+ void run(PassRunner* runner, Module* module) override {}
+};
+
+Pass* createModAsyncifyNeverUnwindPass() {
+ return new ModAsyncify<false, true, false>();
+}
+
} // namespace wasm
diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp
index 7d17510fc..0ac70aa59 100644
--- a/src/passes/pass.cpp
+++ b/src/passes/pass.cpp
@@ -180,6 +180,13 @@ void PassRegistry::registerPasses() {
"minifies both import and export names, and emits a mapping to "
"the minified ones",
createMinifyImportsAndExportsPass);
+ registerPass("mod-asyncify-always-and-only-unwind",
+ "apply the assumption that asyncify imports always unwind, "
+ "and we never rewind",
+ createModAsyncifyAlwaysOnlyUnwindPass);
+ registerPass("mod-asyncify-never-unwind",
+ "apply the assumption that asyncify never unwinds",
+ createModAsyncifyNeverUnwindPass);
registerPass("nm", "name list", createNameListPass);
registerPass("no-exit-runtime",
"removes calls to atexit(), which is valid if the C runtime "
diff --git a/src/passes/passes.h b/src/passes/passes.h
index 6342ea15b..6732f1114 100644
--- a/src/passes/passes.h
+++ b/src/passes/passes.h
@@ -69,6 +69,8 @@ Pass* createOptimizeAddedConstantsPropagatePass();
Pass* createOptimizeInstructionsPass();
Pass* createOptimizeStackIRPass();
Pass* createPickLoadSignsPass();
+Pass* createModAsyncifyAlwaysOnlyUnwindPass();
+Pass* createModAsyncifyNeverUnwindPass();
Pass* createPostEmscriptenPass();
Pass* createPrecomputePass();
Pass* createPrecomputePropagatePass();
diff --git a/test/passes/asyncify_mod-asyncify-always-and-only-unwind.txt b/test/passes/asyncify_mod-asyncify-always-and-only-unwind.txt
new file mode 100644
index 000000000..c7e35c2f8
--- /dev/null
+++ b/test/passes/asyncify_mod-asyncify-always-and-only-unwind.txt
@@ -0,0 +1,516 @@
+(module
+ (type $FUNCSIG$v (func))
+ (type $FUNCSIG$i (func (result i32)))
+ (type $FUNCSIG$vi (func (param i32)))
+ (import "env" "import" (func $import))
+ (import "env" "import2" (func $import2 (result i32)))
+ (import "env" "import3" (func $import3 (param i32)))
+ (memory $0 1 2)
+ (global $__asyncify_state (mut i32) (i32.const 0))
+ (global $__asyncify_data (mut i32) (i32.const 0))
+ (export "asyncify_start_unwind" (func $asyncify_start_unwind))
+ (export "asyncify_stop_unwind" (func $asyncify_stop_unwind))
+ (export "asyncify_start_rewind" (func $asyncify_start_rewind))
+ (export "asyncify_stop_rewind" (func $asyncify_stop_rewind))
+ (func $calls-import (; 3 ;) (type $FUNCSIG$v)
+ (local $0 i32)
+ (local $1 i32)
+ (if
+ (i32.const 0)
+ (nop)
+ )
+ (local.set $0
+ (block $__asyncify_unwind (result i32)
+ (block
+ (block
+ (if
+ (i32.const 0)
+ (block
+ (i32.store
+ (global.get $__asyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.const -4)
+ )
+ )
+ (local.set $1
+ (i32.load
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ )
+ )
+ )
+ )
+ (block
+ (if
+ (if (result i32)
+ (i32.eq
+ (global.get $__asyncify_state)
+ (i32.const 0)
+ )
+ (i32.const 1)
+ (i32.eq
+ (local.get $1)
+ (i32.const 0)
+ )
+ )
+ (block
+ (call $import)
+ (if
+ (i32.const 1)
+ (br $__asyncify_unwind
+ (i32.const 0)
+ )
+ )
+ )
+ )
+ (if
+ (i32.eq
+ (global.get $__asyncify_state)
+ (i32.const 0)
+ )
+ (nop)
+ )
+ )
+ )
+ (return)
+ )
+ )
+ )
+ (block
+ (i32.store
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (local.get $0)
+ )
+ (i32.store
+ (global.get $__asyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.const 4)
+ )
+ )
+ )
+ (nop)
+ )
+ (func $calls-import2 (; 4 ;) (type $FUNCSIG$i) (result i32)
+ (local $temp i32)
+ (local $1 i32)
+ (local $2 i32)
+ (local $3 i32)
+ (local $4 i32)
+ (local $5 i32)
+ (local $6 i32)
+ (local $7 i32)
+ (local $8 i32)
+ (local $9 i32)
+ (if
+ (i32.const 0)
+ (block
+ (i32.store
+ (global.get $__asyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.const -20)
+ )
+ )
+ (local.set $8
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ )
+ (local.set $temp
+ (i32.load
+ (local.get $8)
+ )
+ )
+ (local.set $1
+ (i32.load offset=4
+ (local.get $8)
+ )
+ )
+ (local.set $2
+ (i32.load offset=8
+ (local.get $8)
+ )
+ )
+ (local.set $3
+ (i32.load offset=12
+ (local.get $8)
+ )
+ )
+ (local.set $4
+ (i32.load offset=16
+ (local.get $8)
+ )
+ )
+ )
+ )
+ (local.set $5
+ (block $__asyncify_unwind (result i32)
+ (block
+ (block
+ (if
+ (i32.const 0)
+ (block
+ (i32.store
+ (global.get $__asyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.const -4)
+ )
+ )
+ (local.set $6
+ (i32.load
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ )
+ )
+ )
+ )
+ (block
+ (if
+ (if (result i32)
+ (i32.eq
+ (global.get $__asyncify_state)
+ (i32.const 0)
+ )
+ (i32.const 1)
+ (i32.eq
+ (local.get $6)
+ (i32.const 0)
+ )
+ )
+ (block
+ (local.set $7
+ (call $import2)
+ )
+ (if
+ (i32.const 1)
+ (br $__asyncify_unwind
+ (i32.const 0)
+ )
+ (local.set $1
+ (local.get $7)
+ )
+ )
+ )
+ )
+ (if
+ (i32.eq
+ (global.get $__asyncify_state)
+ (i32.const 0)
+ )
+ (block
+ (local.set $temp
+ (local.get $1)
+ )
+ (nop)
+ (local.set $2
+ (local.get $temp)
+ )
+ (return
+ (local.get $2)
+ )
+ )
+ )
+ (nop)
+ (nop)
+ (nop)
+ )
+ (unreachable)
+ )
+ (unreachable)
+ )
+ )
+ )
+ (block
+ (i32.store
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (local.get $5)
+ )
+ (i32.store
+ (global.get $__asyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.const 4)
+ )
+ )
+ )
+ (block
+ (local.set $9
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ )
+ (i32.store
+ (local.get $9)
+ (local.get $temp)
+ )
+ (i32.store offset=4
+ (local.get $9)
+ (local.get $1)
+ )
+ (i32.store offset=8
+ (local.get $9)
+ (local.get $2)
+ )
+ (i32.store offset=12
+ (local.get $9)
+ (local.get $3)
+ )
+ (i32.store offset=16
+ (local.get $9)
+ (local.get $4)
+ )
+ (i32.store
+ (global.get $__asyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.const 20)
+ )
+ )
+ )
+ (i32.const 0)
+ )
+ (func $calls-import2-drop (; 5 ;) (type $FUNCSIG$v)
+ (local $0 i32)
+ (local $1 i32)
+ (local $2 i32)
+ (local $3 i32)
+ (local $4 i32)
+ (local $5 i32)
+ (if
+ (i32.const 0)
+ (block
+ (i32.store
+ (global.get $__asyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.const -4)
+ )
+ )
+ (local.set $4
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ )
+ (local.set $0
+ (i32.load
+ (local.get $4)
+ )
+ )
+ )
+ )
+ (local.set $1
+ (block $__asyncify_unwind (result i32)
+ (block
+ (block
+ (if
+ (i32.const 0)
+ (block
+ (i32.store
+ (global.get $__asyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.const -4)
+ )
+ )
+ (local.set $2
+ (i32.load
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ )
+ )
+ )
+ )
+ (block
+ (if
+ (if (result i32)
+ (i32.eq
+ (global.get $__asyncify_state)
+ (i32.const 0)
+ )
+ (i32.const 1)
+ (i32.eq
+ (local.get $2)
+ (i32.const 0)
+ )
+ )
+ (block
+ (local.set $3
+ (call $import2)
+ )
+ (if
+ (i32.const 1)
+ (br $__asyncify_unwind
+ (i32.const 0)
+ )
+ (local.set $0
+ (local.get $3)
+ )
+ )
+ )
+ )
+ (if
+ (i32.eq
+ (global.get $__asyncify_state)
+ (i32.const 0)
+ )
+ (block
+ (drop
+ (local.get $0)
+ )
+ (nop)
+ )
+ )
+ (nop)
+ )
+ )
+ (return)
+ )
+ )
+ )
+ (block
+ (i32.store
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (local.get $1)
+ )
+ (i32.store
+ (global.get $__asyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.const 4)
+ )
+ )
+ )
+ (block
+ (local.set $5
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ )
+ (i32.store
+ (local.get $5)
+ (local.get $0)
+ )
+ (i32.store
+ (global.get $__asyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.const 4)
+ )
+ )
+ )
+ )
+ (func $calls-nothing (; 6 ;) (type $FUNCSIG$v)
+ (local $0 i32)
+ (local.set $0
+ (i32.eqz
+ (i32.const 17)
+ )
+ )
+ (drop
+ (local.get $0)
+ )
+ (nop)
+ )
+ (func $asyncify_start_unwind (; 7 ;) (param $0 i32)
+ (global.set $__asyncify_state
+ (i32.const 1)
+ )
+ (global.set $__asyncify_data
+ (local.get $0)
+ )
+ (if
+ (i32.gt_u
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.load offset=4
+ (global.get $__asyncify_data)
+ )
+ )
+ (unreachable)
+ )
+ )
+ (func $asyncify_stop_unwind (; 8 ;)
+ (global.set $__asyncify_state
+ (i32.const 0)
+ )
+ (if
+ (i32.gt_u
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.load offset=4
+ (global.get $__asyncify_data)
+ )
+ )
+ (unreachable)
+ )
+ )
+ (func $asyncify_start_rewind (; 9 ;) (param $0 i32)
+ (global.set $__asyncify_state
+ (i32.const 2)
+ )
+ (global.set $__asyncify_data
+ (local.get $0)
+ )
+ (if
+ (i32.gt_u
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.load offset=4
+ (global.get $__asyncify_data)
+ )
+ )
+ (unreachable)
+ )
+ )
+ (func $asyncify_stop_rewind (; 10 ;)
+ (global.set $__asyncify_state
+ (i32.const 0)
+ )
+ (if
+ (i32.gt_u
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.load offset=4
+ (global.get $__asyncify_data)
+ )
+ )
+ (unreachable)
+ )
+ )
+)
diff --git a/test/passes/asyncify_mod-asyncify-always-and-only-unwind.wast b/test/passes/asyncify_mod-asyncify-always-and-only-unwind.wast
new file mode 100644
index 000000000..70cb7e8fb
--- /dev/null
+++ b/test/passes/asyncify_mod-asyncify-always-and-only-unwind.wast
@@ -0,0 +1,20 @@
+(module
+ (memory 1 2)
+ (import "env" "import" (func $import))
+ (import "env" "import2" (func $import2 (result i32)))
+ (import "env" "import3" (func $import3 (param i32)))
+ (func $calls-import
+ (call $import)
+ )
+ (func $calls-import2 (result i32)
+ (local $temp i32)
+ (local.set $temp (call $import2))
+ (return (local.get $temp))
+ )
+ (func $calls-import2-drop
+ (drop (call $import2))
+ )
+ (func $calls-nothing
+ (drop (i32.eqz (i32.const 17)))
+ )
+)
diff --git a/test/passes/asyncify_mod-asyncify-always-and-only-unwind_O.txt b/test/passes/asyncify_mod-asyncify-always-and-only-unwind_O.txt
new file mode 100644
index 000000000..3f83675e8
--- /dev/null
+++ b/test/passes/asyncify_mod-asyncify-always-and-only-unwind_O.txt
@@ -0,0 +1,88 @@
+(module
+ (type $FUNCSIG$v (func))
+ (import "env" "import" (func $import))
+ (memory $0 1 2)
+ (global $__asyncify_state (mut i32) (i32.const 0))
+ (global $__asyncify_data (mut i32) (i32.const 0))
+ (export "calls-import" (func $calls-import))
+ (export "calls-import2" (func $calls-import))
+ (export "calls-import2-drop" (func $calls-import))
+ (export "calls-nothing" (func $calls-import))
+ (export "asyncify_start_unwind" (func $asyncify_start_unwind))
+ (export "asyncify_stop_unwind" (func $asyncify_stop_unwind))
+ (export "asyncify_start_rewind" (func $asyncify_start_rewind))
+ (export "asyncify_stop_rewind" (func $asyncify_stop_unwind))
+ (func $calls-import (; 1 ;) (; has Stack IR ;) (type $FUNCSIG$v)
+ (local $0 i32)
+ (call $import)
+ (i32.store
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (local.get $0)
+ )
+ (i32.store
+ (global.get $__asyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.const 4)
+ )
+ )
+ )
+ (func $asyncify_start_unwind (; 2 ;) (; has Stack IR ;) (param $0 i32)
+ (global.set $__asyncify_state
+ (i32.const 1)
+ )
+ (global.set $__asyncify_data
+ (local.get $0)
+ )
+ (if
+ (i32.gt_u
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.load offset=4
+ (global.get $__asyncify_data)
+ )
+ )
+ (unreachable)
+ )
+ )
+ (func $asyncify_stop_unwind (; 3 ;) (; has Stack IR ;)
+ (global.set $__asyncify_state
+ (i32.const 0)
+ )
+ (if
+ (i32.gt_u
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.load offset=4
+ (global.get $__asyncify_data)
+ )
+ )
+ (unreachable)
+ )
+ )
+ (func $asyncify_start_rewind (; 4 ;) (; has Stack IR ;) (param $0 i32)
+ (global.set $__asyncify_state
+ (i32.const 2)
+ )
+ (global.set $__asyncify_data
+ (local.get $0)
+ )
+ (if
+ (i32.gt_u
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.load offset=4
+ (global.get $__asyncify_data)
+ )
+ )
+ (unreachable)
+ )
+ )
+)
diff --git a/test/passes/asyncify_mod-asyncify-always-and-only-unwind_O.wast b/test/passes/asyncify_mod-asyncify-always-and-only-unwind_O.wast
new file mode 100644
index 000000000..5d8f2e4b7
--- /dev/null
+++ b/test/passes/asyncify_mod-asyncify-always-and-only-unwind_O.wast
@@ -0,0 +1,24 @@
+(module
+ (memory 1 2)
+ (import "env" "import" (func $import))
+ (import "env" "import2" (func $import2 (result i32)))
+ (import "env" "import3" (func $import3 (param i32)))
+ (export "calls-import" (func $calls-import))
+ (export "calls-import2" (func $calls-import))
+ (export "calls-import2-drop" (func $calls-import))
+ (export "calls-nothing" (func $calls-import))
+ (func $calls-import
+ (call $import)
+ )
+ (func $calls-import2 (result i32)
+ (local $temp i32)
+ (local.set $temp (call $import2))
+ (return (local.get $temp))
+ )
+ (func $calls-import2-drop
+ (drop (call $import2))
+ )
+ (func $calls-nothing
+ (drop (i32.eqz (i32.const 17)))
+ )
+)
diff --git a/test/passes/asyncify_mod-asyncify-never-unwind.txt b/test/passes/asyncify_mod-asyncify-never-unwind.txt
new file mode 100644
index 000000000..8647f5426
--- /dev/null
+++ b/test/passes/asyncify_mod-asyncify-never-unwind.txt
@@ -0,0 +1,534 @@
+(module
+ (type $FUNCSIG$v (func))
+ (type $FUNCSIG$i (func (result i32)))
+ (type $FUNCSIG$vi (func (param i32)))
+ (import "env" "import" (func $import))
+ (import "env" "import2" (func $import2 (result i32)))
+ (import "env" "import3" (func $import3 (param i32)))
+ (memory $0 1 2)
+ (global $__asyncify_state (mut i32) (i32.const 0))
+ (global $__asyncify_data (mut i32) (i32.const 0))
+ (export "asyncify_start_unwind" (func $asyncify_start_unwind))
+ (export "asyncify_stop_unwind" (func $asyncify_stop_unwind))
+ (export "asyncify_start_rewind" (func $asyncify_start_rewind))
+ (export "asyncify_stop_rewind" (func $asyncify_stop_rewind))
+ (func $calls-import (; 3 ;) (type $FUNCSIG$v)
+ (local $0 i32)
+ (local $1 i32)
+ (if
+ (i32.eq
+ (global.get $__asyncify_state)
+ (i32.const 2)
+ )
+ (nop)
+ )
+ (local.set $0
+ (block $__asyncify_unwind (result i32)
+ (block
+ (block
+ (if
+ (i32.eq
+ (global.get $__asyncify_state)
+ (i32.const 2)
+ )
+ (block
+ (i32.store
+ (global.get $__asyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.const -4)
+ )
+ )
+ (local.set $1
+ (i32.load
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ )
+ )
+ )
+ )
+ (block
+ (if
+ (if (result i32)
+ (i32.eq
+ (global.get $__asyncify_state)
+ (i32.const 0)
+ )
+ (i32.const 1)
+ (i32.eq
+ (local.get $1)
+ (i32.const 0)
+ )
+ )
+ (block
+ (call $import)
+ (if
+ (i32.const 0)
+ (br $__asyncify_unwind
+ (i32.const 0)
+ )
+ )
+ )
+ )
+ (if
+ (i32.eq
+ (global.get $__asyncify_state)
+ (i32.const 0)
+ )
+ (nop)
+ )
+ )
+ )
+ (return)
+ )
+ )
+ )
+ (block
+ (i32.store
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (local.get $0)
+ )
+ (i32.store
+ (global.get $__asyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.const 4)
+ )
+ )
+ )
+ (nop)
+ )
+ (func $calls-import2 (; 4 ;) (type $FUNCSIG$i) (result i32)
+ (local $temp i32)
+ (local $1 i32)
+ (local $2 i32)
+ (local $3 i32)
+ (local $4 i32)
+ (local $5 i32)
+ (local $6 i32)
+ (local $7 i32)
+ (local $8 i32)
+ (local $9 i32)
+ (if
+ (i32.eq
+ (global.get $__asyncify_state)
+ (i32.const 2)
+ )
+ (block
+ (i32.store
+ (global.get $__asyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.const -20)
+ )
+ )
+ (local.set $8
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ )
+ (local.set $temp
+ (i32.load
+ (local.get $8)
+ )
+ )
+ (local.set $1
+ (i32.load offset=4
+ (local.get $8)
+ )
+ )
+ (local.set $2
+ (i32.load offset=8
+ (local.get $8)
+ )
+ )
+ (local.set $3
+ (i32.load offset=12
+ (local.get $8)
+ )
+ )
+ (local.set $4
+ (i32.load offset=16
+ (local.get $8)
+ )
+ )
+ )
+ )
+ (local.set $5
+ (block $__asyncify_unwind (result i32)
+ (block
+ (block
+ (if
+ (i32.eq
+ (global.get $__asyncify_state)
+ (i32.const 2)
+ )
+ (block
+ (i32.store
+ (global.get $__asyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.const -4)
+ )
+ )
+ (local.set $6
+ (i32.load
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ )
+ )
+ )
+ )
+ (block
+ (if
+ (if (result i32)
+ (i32.eq
+ (global.get $__asyncify_state)
+ (i32.const 0)
+ )
+ (i32.const 1)
+ (i32.eq
+ (local.get $6)
+ (i32.const 0)
+ )
+ )
+ (block
+ (local.set $7
+ (call $import2)
+ )
+ (if
+ (i32.const 0)
+ (br $__asyncify_unwind
+ (i32.const 0)
+ )
+ (local.set $1
+ (local.get $7)
+ )
+ )
+ )
+ )
+ (if
+ (i32.eq
+ (global.get $__asyncify_state)
+ (i32.const 0)
+ )
+ (block
+ (local.set $temp
+ (local.get $1)
+ )
+ (nop)
+ (local.set $2
+ (local.get $temp)
+ )
+ (return
+ (local.get $2)
+ )
+ )
+ )
+ (nop)
+ (nop)
+ (nop)
+ )
+ (unreachable)
+ )
+ (unreachable)
+ )
+ )
+ )
+ (block
+ (i32.store
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (local.get $5)
+ )
+ (i32.store
+ (global.get $__asyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.const 4)
+ )
+ )
+ )
+ (block
+ (local.set $9
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ )
+ (i32.store
+ (local.get $9)
+ (local.get $temp)
+ )
+ (i32.store offset=4
+ (local.get $9)
+ (local.get $1)
+ )
+ (i32.store offset=8
+ (local.get $9)
+ (local.get $2)
+ )
+ (i32.store offset=12
+ (local.get $9)
+ (local.get $3)
+ )
+ (i32.store offset=16
+ (local.get $9)
+ (local.get $4)
+ )
+ (i32.store
+ (global.get $__asyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.const 20)
+ )
+ )
+ )
+ (i32.const 0)
+ )
+ (func $calls-import2-drop (; 5 ;) (type $FUNCSIG$v)
+ (local $0 i32)
+ (local $1 i32)
+ (local $2 i32)
+ (local $3 i32)
+ (local $4 i32)
+ (local $5 i32)
+ (if
+ (i32.eq
+ (global.get $__asyncify_state)
+ (i32.const 2)
+ )
+ (block
+ (i32.store
+ (global.get $__asyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.const -4)
+ )
+ )
+ (local.set $4
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ )
+ (local.set $0
+ (i32.load
+ (local.get $4)
+ )
+ )
+ )
+ )
+ (local.set $1
+ (block $__asyncify_unwind (result i32)
+ (block
+ (block
+ (if
+ (i32.eq
+ (global.get $__asyncify_state)
+ (i32.const 2)
+ )
+ (block
+ (i32.store
+ (global.get $__asyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.const -4)
+ )
+ )
+ (local.set $2
+ (i32.load
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ )
+ )
+ )
+ )
+ (block
+ (if
+ (if (result i32)
+ (i32.eq
+ (global.get $__asyncify_state)
+ (i32.const 0)
+ )
+ (i32.const 1)
+ (i32.eq
+ (local.get $2)
+ (i32.const 0)
+ )
+ )
+ (block
+ (local.set $3
+ (call $import2)
+ )
+ (if
+ (i32.const 0)
+ (br $__asyncify_unwind
+ (i32.const 0)
+ )
+ (local.set $0
+ (local.get $3)
+ )
+ )
+ )
+ )
+ (if
+ (i32.eq
+ (global.get $__asyncify_state)
+ (i32.const 0)
+ )
+ (block
+ (drop
+ (local.get $0)
+ )
+ (nop)
+ )
+ )
+ (nop)
+ )
+ )
+ (return)
+ )
+ )
+ )
+ (block
+ (i32.store
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (local.get $1)
+ )
+ (i32.store
+ (global.get $__asyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.const 4)
+ )
+ )
+ )
+ (block
+ (local.set $5
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ )
+ (i32.store
+ (local.get $5)
+ (local.get $0)
+ )
+ (i32.store
+ (global.get $__asyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.const 4)
+ )
+ )
+ )
+ )
+ (func $calls-nothing (; 6 ;) (type $FUNCSIG$v)
+ (local $0 i32)
+ (local.set $0
+ (i32.eqz
+ (i32.const 17)
+ )
+ )
+ (drop
+ (local.get $0)
+ )
+ (nop)
+ )
+ (func $asyncify_start_unwind (; 7 ;) (param $0 i32)
+ (global.set $__asyncify_state
+ (i32.const 1)
+ )
+ (global.set $__asyncify_data
+ (local.get $0)
+ )
+ (if
+ (i32.gt_u
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.load offset=4
+ (global.get $__asyncify_data)
+ )
+ )
+ (unreachable)
+ )
+ )
+ (func $asyncify_stop_unwind (; 8 ;)
+ (global.set $__asyncify_state
+ (i32.const 0)
+ )
+ (if
+ (i32.gt_u
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.load offset=4
+ (global.get $__asyncify_data)
+ )
+ )
+ (unreachable)
+ )
+ )
+ (func $asyncify_start_rewind (; 9 ;) (param $0 i32)
+ (global.set $__asyncify_state
+ (i32.const 2)
+ )
+ (global.set $__asyncify_data
+ (local.get $0)
+ )
+ (if
+ (i32.gt_u
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.load offset=4
+ (global.get $__asyncify_data)
+ )
+ )
+ (unreachable)
+ )
+ )
+ (func $asyncify_stop_rewind (; 10 ;)
+ (global.set $__asyncify_state
+ (i32.const 0)
+ )
+ (if
+ (i32.gt_u
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.load offset=4
+ (global.get $__asyncify_data)
+ )
+ )
+ (unreachable)
+ )
+ )
+)
diff --git a/test/passes/asyncify_mod-asyncify-never-unwind.wast b/test/passes/asyncify_mod-asyncify-never-unwind.wast
new file mode 100644
index 000000000..70cb7e8fb
--- /dev/null
+++ b/test/passes/asyncify_mod-asyncify-never-unwind.wast
@@ -0,0 +1,20 @@
+(module
+ (memory 1 2)
+ (import "env" "import" (func $import))
+ (import "env" "import2" (func $import2 (result i32)))
+ (import "env" "import3" (func $import3 (param i32)))
+ (func $calls-import
+ (call $import)
+ )
+ (func $calls-import2 (result i32)
+ (local $temp i32)
+ (local.set $temp (call $import2))
+ (return (local.get $temp))
+ )
+ (func $calls-import2-drop
+ (drop (call $import2))
+ )
+ (func $calls-nothing
+ (drop (i32.eqz (i32.const 17)))
+ )
+)
diff --git a/test/passes/asyncify_mod-asyncify-never-unwind_O.txt b/test/passes/asyncify_mod-asyncify-never-unwind_O.txt
new file mode 100644
index 000000000..b185f5c6b
--- /dev/null
+++ b/test/passes/asyncify_mod-asyncify-never-unwind_O.txt
@@ -0,0 +1,103 @@
+(module
+ (type $FUNCSIG$v (func))
+ (import "env" "import" (func $import))
+ (memory $0 1 2)
+ (global $__asyncify_state (mut i32) (i32.const 0))
+ (global $__asyncify_data (mut i32) (i32.const 0))
+ (export "calls-import" (func $calls-import))
+ (export "calls-import2" (func $calls-import))
+ (export "calls-import2-drop" (func $calls-import))
+ (export "calls-nothing" (func $calls-import))
+ (export "asyncify_start_unwind" (func $asyncify_start_unwind))
+ (export "asyncify_stop_unwind" (func $asyncify_stop_unwind))
+ (export "asyncify_start_rewind" (func $asyncify_start_rewind))
+ (export "asyncify_stop_rewind" (func $asyncify_stop_unwind))
+ (func $calls-import (; 1 ;) (; has Stack IR ;) (type $FUNCSIG$v)
+ (if
+ (select
+ (i32.eqz
+ (if (result i32)
+ (i32.eq
+ (global.get $__asyncify_state)
+ (i32.const 2)
+ )
+ (block (result i32)
+ (i32.store
+ (global.get $__asyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.const -4)
+ )
+ )
+ (i32.load
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ )
+ )
+ (i32.const 0)
+ )
+ )
+ (i32.const 1)
+ (global.get $__asyncify_state)
+ )
+ (call $import)
+ )
+ )
+ (func $asyncify_start_unwind (; 2 ;) (; has Stack IR ;) (param $0 i32)
+ (global.set $__asyncify_state
+ (i32.const 1)
+ )
+ (global.set $__asyncify_data
+ (local.get $0)
+ )
+ (if
+ (i32.gt_u
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.load offset=4
+ (global.get $__asyncify_data)
+ )
+ )
+ (unreachable)
+ )
+ )
+ (func $asyncify_stop_unwind (; 3 ;) (; has Stack IR ;)
+ (global.set $__asyncify_state
+ (i32.const 0)
+ )
+ (if
+ (i32.gt_u
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.load offset=4
+ (global.get $__asyncify_data)
+ )
+ )
+ (unreachable)
+ )
+ )
+ (func $asyncify_start_rewind (; 4 ;) (; has Stack IR ;) (param $0 i32)
+ (global.set $__asyncify_state
+ (i32.const 2)
+ )
+ (global.set $__asyncify_data
+ (local.get $0)
+ )
+ (if
+ (i32.gt_u
+ (i32.load
+ (global.get $__asyncify_data)
+ )
+ (i32.load offset=4
+ (global.get $__asyncify_data)
+ )
+ )
+ (unreachable)
+ )
+ )
+)
diff --git a/test/passes/asyncify_mod-asyncify-never-unwind_O.wast b/test/passes/asyncify_mod-asyncify-never-unwind_O.wast
new file mode 100644
index 000000000..5d8f2e4b7
--- /dev/null
+++ b/test/passes/asyncify_mod-asyncify-never-unwind_O.wast
@@ -0,0 +1,24 @@
+(module
+ (memory 1 2)
+ (import "env" "import" (func $import))
+ (import "env" "import2" (func $import2 (result i32)))
+ (import "env" "import3" (func $import3 (param i32)))
+ (export "calls-import" (func $calls-import))
+ (export "calls-import2" (func $calls-import))
+ (export "calls-import2-drop" (func $calls-import))
+ (export "calls-nothing" (func $calls-import))
+ (func $calls-import
+ (call $import)
+ )
+ (func $calls-import2 (result i32)
+ (local $temp i32)
+ (local.set $temp (call $import2))
+ (return (local.get $temp))
+ )
+ (func $calls-import2-drop
+ (drop (call $import2))
+ )
+ (func $calls-nothing
+ (drop (i32.eqz (i32.const 17)))
+ )
+)