summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2019-06-21 14:48:06 -0700
committerGitHub <noreply@github.com>2019-06-21 14:48:06 -0700
commitcbc7e868d85a81e1a2f802b633d3cf323a14338f (patch)
treed59a65d99f4d10b281da4ffe6612ea25b8f6e820
parent3f797c82e49bb2a5e5f6bcd2393e369ef618a49b (diff)
downloadbinaryen-cbc7e868d85a81e1a2f802b633d3cf323a14338f.tar.gz
binaryen-cbc7e868d85a81e1a2f802b633d3cf323a14338f.tar.bz2
binaryen-cbc7e868d85a81e1a2f802b633d3cf323a14338f.zip
Bysyncify: Don't instrument functions that call bysyncify_* directly (#2179)
Those functions are assumed to be part of the runtime. Instrumenting them would mean nothing can work. With this fix, bysyncify is useful with pure wasm, and not just through imports.
-rw-r--r--src/passes/Bysyncify.cpp64
-rw-r--r--test/passes/bysyncify.txt372
-rw-r--r--test/passes/bysyncify_pass-arg=bysyncify-ignore-indirect.txt478
-rw-r--r--test/passes/bysyncify_pass-arg=bysyncify-ignore-indirect.wast26
-rw-r--r--test/passes/bysyncify_pass-arg=bysyncify-imports@env.import,env.import2.txt346
-rw-r--r--test/unit/input/bysyncify-pure.txt17
-rw-r--r--test/unit/input/bysyncify-pure.wast59
-rw-r--r--test/unit/test_bysyncify.py11
8 files changed, 678 insertions, 695 deletions
diff --git a/src/passes/Bysyncify.cpp b/src/passes/Bysyncify.cpp
index dcc543c5e..29b3cf242 100644
--- a/src/passes/Bysyncify.cpp
+++ b/src/passes/Bysyncify.cpp
@@ -155,7 +155,14 @@
// around that, you can create imports to bysyncify.start_unwind,
// bysyncify.stop_unwind, bysyncify.start_rewind, and bysyncify.stop_rewind;
// if those exist when this pass runs then it will turn those into direct
-// calls to the functions that it creates.
+// calls to the functions that it creates. Note that when doing everything
+// in wasm like this, Bysyncify must not instrument your "runtime" support
+// code, that is, the code that initiates unwinds and rewinds and stops them.
+// If it did, the unwind would not stop until you left the wasm module
+// entirely, etc. Therefore we do not instrument a function if it has
+// a call to the four bysyncify_* methods. Note that you may need to disable
+// inlining if that would cause code that does need to be instrumented
+// show up in that runtime code.
//
// To use this API, call bysyncify_start_unwind when you want to. The call
// stack will then be unwound, and so execution will resume in the JS or
@@ -237,7 +244,19 @@ class ModuleAnalyzer {
bool canIndirectChangeState;
struct Info {
+ // If this function can start an unwind/rewind.
bool canChangeState = false;
+ // If this function is part of the runtime that receives an unwinding
+ // and starts a rewinding. If so, we do not instrument it, see above.
+ // This is only relevant when handling things entirely inside wasm,
+ // as opposed to imports.
+ bool isBottomMostRuntime = false;
+ // If this function is part of the runtime that starts an unwinding
+ // and stops a rewinding. If so, we do not instrument it, see above.
+ // The difference between the top-most and bottom-most runtime is that
+ // the top-most part is still marked as changing the state; so things
+ // that call it are instrumented. This is not done for the bottom.
+ bool isTopMostRuntime = false;
std::set<Function*> callsTo;
std::set<Function*> calledBy;
};
@@ -257,10 +276,9 @@ public:
ModuleUtils::ParallelFunctionMap<Info> scanner(
module, [&](Function* func, Info& info) {
if (func->imported()) {
- // The bysyncify imports can definitely change the state.
+ // The relevant bysyncify imports can definitely change the state.
if (func->module == BYSYNCIFY &&
- (func->base == START_UNWIND || func->base == STOP_UNWIND ||
- func->base == START_REWIND || func->base == STOP_REWIND)) {
+ (func->base == START_UNWIND || func->base == STOP_REWIND)) {
info.canChangeState = true;
} else {
info.canChangeState =
@@ -275,18 +293,22 @@ public:
// Redirect the imports to the functions we'll add later.
if (target->base == START_UNWIND) {
curr->target = BYSYNCIFY_START_UNWIND;
+ info->canChangeState = true;
+ info->isTopMostRuntime = true;
} else if (target->base == STOP_UNWIND) {
curr->target = BYSYNCIFY_STOP_UNWIND;
+ info->isBottomMostRuntime = true;
} else if (target->base == START_REWIND) {
curr->target = BYSYNCIFY_START_REWIND;
+ info->isBottomMostRuntime = true;
} else if (target->base == STOP_REWIND) {
curr->target = BYSYNCIFY_STOP_REWIND;
- // TODO: in theory, this does not change the state
+ info->canChangeState = true;
+ info->isTopMostRuntime = true;
} else {
Fatal() << "call to unidenfied bysyncify import: "
<< target->base;
}
- info->canChangeState = true;
return;
}
info->callsTo.insert(target);
@@ -306,7 +328,15 @@ public:
walker.module = &module;
walker.canIndirectChangeState = canIndirectChangeState;
walker.walk(func->body);
+
+ if (info.isBottomMostRuntime) {
+ info.canChangeState = false;
+ // TODO: issue warnings on suspicious things, like a function in
+ // the bottom-most runtime also doing top-most runtime stuff
+ // like starting and unwinding.
+ }
});
+
map.swap(scanner.map);
// Remove the bysyncify imports, if any.
@@ -338,7 +368,7 @@ public:
while (!work.empty()) {
auto* func = work.pop();
for (auto* caller : map[func].calledBy) {
- if (!map[caller].canChangeState) {
+ if (!map[caller].canChangeState && !map[caller].isBottomMostRuntime) {
map[caller].canChangeState = true;
work.push(caller);
}
@@ -346,7 +376,10 @@ public:
}
}
- bool canChangeState(Function* func) { return map[func].canChangeState; }
+ bool needsInstrumentation(Function* func) {
+ auto& info = map[func];
+ return info.canChangeState && !info.isTopMostRuntime;
+ }
bool canChangeState(Expression* curr) {
// Look inside to see if we call any of the things we know can change the
@@ -357,14 +390,17 @@ public:
// We only implement these at the very end, but we know that they
// definitely change the state.
if (curr->target == BYSYNCIFY_START_UNWIND ||
- curr->target == BYSYNCIFY_STOP_UNWIND ||
- curr->target == BYSYNCIFY_START_REWIND ||
curr->target == BYSYNCIFY_STOP_REWIND ||
curr->target == BYSYNCIFY_GET_CALL_INDEX ||
curr->target == BYSYNCIFY_CHECK_CALL_INDEX) {
canChangeState = true;
return;
}
+ if (curr->target == BYSYNCIFY_STOP_UNWIND ||
+ curr->target == BYSYNCIFY_START_REWIND) {
+ isBottomMostRuntime = true;
+ return;
+ }
// The target may not exist if it is one of our temporary intrinsics.
auto* target = module->getFunctionOrNull(curr->target);
if (target && (*map)[target].canChangeState) {
@@ -381,12 +417,16 @@ public:
Map* map;
bool canIndirectChangeState;
bool canChangeState = false;
+ bool isBottomMostRuntime = false;
};
Walker walker;
walker.module = &module;
walker.map = &map;
walker.canIndirectChangeState = canIndirectChangeState;
walker.walk(curr);
+ if (walker.isBottomMostRuntime) {
+ walker.canChangeState = false;
+ }
return walker.canChangeState;
}
};
@@ -451,7 +491,7 @@ struct BysyncifyFlow : public Pass {
module = module_;
// If the function cannot change our state, we have nothing to do -
// we will never unwind or rewind the stack here.
- if (!analyzer->canChangeState(func)) {
+ if (!analyzer->needsInstrumentation(func)) {
return;
}
// Rewrite the function body.
@@ -670,7 +710,7 @@ struct BysyncifyLocals : public WalkerPass<PostWalker<BysyncifyLocals>> {
void doWalkFunction(Function* func) {
// If the function cannot change our state, we have nothing to do -
// we will never unwind or rewind the stack here.
- if (!analyzer->canChangeState(func->body)) {
+ if (!analyzer->needsInstrumentation(func)) {
return;
}
// Note the locals we want to preserve, before we add any more helpers.
diff --git a/test/passes/bysyncify.txt b/test/passes/bysyncify.txt
index 2ab682149..814dd05d0 100644
--- a/test/passes/bysyncify.txt
+++ b/test/passes/bysyncify.txt
@@ -12,229 +12,29 @@
(func $do_sleep (; 0 ;) (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 $__bysyncify_state)
- (i32.const 2)
- )
- (block
- (i32.store
- (global.get $__bysyncify_data)
- (i32.add
- (i32.load
- (global.get $__bysyncify_data)
- )
- (i32.const -8)
- )
- )
- (local.set $4
- (i32.load
- (global.get $__bysyncify_data)
- )
- )
- (local.set $0
- (i32.load
- (local.get $4)
- )
- )
- (local.set $1
- (i32.load offset=4
- (local.get $4)
- )
- )
- )
+ (local.set $0
+ (global.get $sleeping)
)
- (local.set $2
- (block $__bysyncify_unwind (result i32)
- (block
- (block
- (if
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 2)
- )
- (block
- (i32.store
- (global.get $__bysyncify_data)
- (i32.add
- (i32.load
- (global.get $__bysyncify_data)
- )
- (i32.const -4)
- )
- )
- (local.set $3
- (i32.load
- (i32.load
- (global.get $__bysyncify_data)
- )
- )
- )
- )
- )
- (block
- (if
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 0)
- )
- (block
- (local.set $0
- (global.get $sleeping)
- )
- (local.set $1
- (i32.eqz
- (local.get $0)
- )
- )
- )
- )
- (nop)
- (block
- (if
- (i32.or
- (local.get $1)
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 2)
- )
- )
- (block
- (if
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 0)
- )
- (global.set $sleeping
- (i32.const 1)
- )
- )
- (if
- (if (result i32)
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 0)
- )
- (i32.const 1)
- (i32.eq
- (local.get $3)
- (i32.const 0)
- )
- )
- (block
- (call $bysyncify_start_unwind
- (i32.const 4)
- )
- (if
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 1)
- )
- (br $__bysyncify_unwind
- (i32.const 0)
- )
- )
- )
- )
- )
- )
- (if
- (i32.or
- (i32.eqz
- (local.get $1)
- )
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 2)
- )
- )
- (block
- (if
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 0)
- )
- (global.set $sleeping
- (i32.const 0)
- )
- )
- (if
- (if (result i32)
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 0)
- )
- (i32.const 1)
- (i32.eq
- (local.get $3)
- (i32.const 1)
- )
- )
- (block
- (call $bysyncify_stop_rewind)
- (if
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 1)
- )
- (br $__bysyncify_unwind
- (i32.const 1)
- )
- )
- )
- )
- )
- )
- )
- )
- )
- (return)
- )
+ (local.set $1
+ (i32.eqz
+ (local.get $0)
)
)
- (block
- (i32.store
- (i32.load
- (global.get $__bysyncify_data)
+ (if
+ (local.get $1)
+ (block
+ (global.set $sleeping
+ (i32.const 1)
)
- (local.get $2)
- )
- (i32.store
- (global.get $__bysyncify_data)
- (i32.add
- (i32.load
- (global.get $__bysyncify_data)
- )
+ (call $bysyncify_start_unwind
(i32.const 4)
)
)
- )
- (block
- (local.set $5
- (i32.load
- (global.get $__bysyncify_data)
- )
- )
- (i32.store
- (local.get $5)
- (local.get $0)
- )
- (i32.store offset=4
- (local.get $5)
- (local.get $1)
- )
- (i32.store
- (global.get $__bysyncify_data)
- (i32.add
- (i32.load
- (global.get $__bysyncify_data)
- )
- (i32.const 8)
+ (block
+ (global.set $sleeping
+ (i32.const 0)
)
+ (call $bysyncify_stop_rewind)
)
)
)
@@ -432,145 +232,11 @@
(nop)
)
(func $second_event (; 4 ;) (type $FUNCSIG$v)
- (local $0 i32)
- (local $1 i32)
- (if
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 2)
- )
- (nop)
- )
- (local.set $0
- (block $__bysyncify_unwind (result i32)
- (block
- (block
- (if
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 2)
- )
- (block
- (i32.store
- (global.get $__bysyncify_data)
- (i32.add
- (i32.load
- (global.get $__bysyncify_data)
- )
- (i32.const -4)
- )
- )
- (local.set $1
- (i32.load
- (i32.load
- (global.get $__bysyncify_data)
- )
- )
- )
- )
- )
- (block
- (if
- (if (result i32)
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 0)
- )
- (i32.const 1)
- (i32.eq
- (local.get $1)
- (i32.const 0)
- )
- )
- (block
- (call $bysyncify_stop_unwind)
- (if
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 1)
- )
- (br $__bysyncify_unwind
- (i32.const 0)
- )
- )
- )
- )
- (if
- (if (result i32)
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 0)
- )
- (i32.const 1)
- (i32.eq
- (local.get $1)
- (i32.const 1)
- )
- )
- (block
- (call $bysyncify_start_rewind
- (i32.const 4)
- )
- (if
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 1)
- )
- (br $__bysyncify_unwind
- (i32.const 1)
- )
- )
- )
- )
- (if
- (if (result i32)
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 0)
- )
- (i32.const 1)
- (i32.eq
- (local.get $1)
- (i32.const 2)
- )
- )
- (block
- (call $work)
- (if
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 1)
- )
- (br $__bysyncify_unwind
- (i32.const 2)
- )
- )
- )
- )
- )
- )
- (return)
- )
- )
- )
- (block
- (i32.store
- (i32.load
- (global.get $__bysyncify_data)
- )
- (local.get $0)
- )
- (i32.store
- (global.get $__bysyncify_data)
- (i32.add
- (i32.load
- (global.get $__bysyncify_data)
- )
- (i32.const 4)
- )
- )
+ (call $bysyncify_stop_unwind)
+ (call $bysyncify_start_rewind
+ (i32.const 4)
)
- (nop)
+ (call $work)
)
(func $never_sleep (; 5 ;) (type $FUNCSIG$v)
(call $stuff)
diff --git a/test/passes/bysyncify_pass-arg=bysyncify-ignore-indirect.txt b/test/passes/bysyncify_pass-arg=bysyncify-ignore-indirect.txt
new file mode 100644
index 000000000..0534e827a
--- /dev/null
+++ b/test/passes/bysyncify_pass-arg=bysyncify-ignore-indirect.txt
@@ -0,0 +1,478 @@
+(module
+ (type $f (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)
+ (table $0 2 2 funcref)
+ (elem (i32.const 0) $calls-import2-drop $calls-import2-drop)
+ (global $__bysyncify_state (mut i32) (i32.const 0))
+ (global $__bysyncify_data (mut i32) (i32.const 0))
+ (export "bysyncify_start_unwind" (func $bysyncify_start_unwind))
+ (export "bysyncify_stop_unwind" (func $bysyncify_stop_unwind))
+ (export "bysyncify_start_rewind" (func $bysyncify_start_rewind))
+ (export "bysyncify_stop_rewind" (func $bysyncify_stop_rewind))
+ (func $calls-import (; 3 ;) (type $f)
+ (local $0 i32)
+ (local $1 i32)
+ (if
+ (i32.eq
+ (global.get $__bysyncify_state)
+ (i32.const 2)
+ )
+ (nop)
+ )
+ (local.set $0
+ (block $__bysyncify_unwind (result i32)
+ (block
+ (block
+ (if
+ (i32.eq
+ (global.get $__bysyncify_state)
+ (i32.const 2)
+ )
+ (block
+ (i32.store
+ (global.get $__bysyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__bysyncify_data)
+ )
+ (i32.const -4)
+ )
+ )
+ (local.set $1
+ (i32.load
+ (i32.load
+ (global.get $__bysyncify_data)
+ )
+ )
+ )
+ )
+ )
+ (if
+ (if (result i32)
+ (i32.eq
+ (global.get $__bysyncify_state)
+ (i32.const 0)
+ )
+ (i32.const 1)
+ (i32.eq
+ (local.get $1)
+ (i32.const 0)
+ )
+ )
+ (block
+ (call $import)
+ (if
+ (i32.eq
+ (global.get $__bysyncify_state)
+ (i32.const 1)
+ )
+ (br $__bysyncify_unwind
+ (i32.const 0)
+ )
+ )
+ )
+ )
+ )
+ (return)
+ )
+ )
+ )
+ (block
+ (i32.store
+ (i32.load
+ (global.get $__bysyncify_data)
+ )
+ (local.get $0)
+ )
+ (i32.store
+ (global.get $__bysyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__bysyncify_data)
+ )
+ (i32.const 4)
+ )
+ )
+ )
+ (nop)
+ )
+ (func $calls-import2-drop (; 4 ;) (type $f)
+ (local $0 i32)
+ (local $1 i32)
+ (local $2 i32)
+ (local $3 i32)
+ (local $4 i32)
+ (if
+ (i32.eq
+ (global.get $__bysyncify_state)
+ (i32.const 2)
+ )
+ (block
+ (i32.store
+ (global.get $__bysyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__bysyncify_data)
+ )
+ (i32.const -4)
+ )
+ )
+ (local.set $3
+ (i32.load
+ (global.get $__bysyncify_data)
+ )
+ )
+ (local.set $0
+ (i32.load
+ (local.get $3)
+ )
+ )
+ )
+ )
+ (local.set $1
+ (block $__bysyncify_unwind (result i32)
+ (block
+ (block
+ (if
+ (i32.eq
+ (global.get $__bysyncify_state)
+ (i32.const 2)
+ )
+ (block
+ (i32.store
+ (global.get $__bysyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__bysyncify_data)
+ )
+ (i32.const -4)
+ )
+ )
+ (local.set $2
+ (i32.load
+ (i32.load
+ (global.get $__bysyncify_data)
+ )
+ )
+ )
+ )
+ )
+ (if
+ (if (result i32)
+ (i32.eq
+ (global.get $__bysyncify_state)
+ (i32.const 0)
+ )
+ (i32.const 1)
+ (i32.eq
+ (local.get $2)
+ (i32.const 0)
+ )
+ )
+ (block
+ (local.set $0
+ (call $import2)
+ )
+ (if
+ (i32.eq
+ (global.get $__bysyncify_state)
+ (i32.const 1)
+ )
+ (br $__bysyncify_unwind
+ (i32.const 0)
+ )
+ )
+ )
+ )
+ )
+ (return)
+ )
+ )
+ )
+ (block
+ (i32.store
+ (i32.load
+ (global.get $__bysyncify_data)
+ )
+ (local.get $1)
+ )
+ (i32.store
+ (global.get $__bysyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__bysyncify_data)
+ )
+ (i32.const 4)
+ )
+ )
+ )
+ (block
+ (local.set $4
+ (i32.load
+ (global.get $__bysyncify_data)
+ )
+ )
+ (i32.store
+ (local.get $4)
+ (local.get $0)
+ )
+ (i32.store
+ (global.get $__bysyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__bysyncify_data)
+ )
+ (i32.const 4)
+ )
+ )
+ )
+ )
+ (func $calls-import2-if-else (; 5 ;) (type $FUNCSIG$vi) (param $x i32)
+ (local $1 i32)
+ (local $2 i32)
+ (local $3 i32)
+ (local $4 i32)
+ (local $5 i32)
+ (if
+ (i32.eq
+ (global.get $__bysyncify_state)
+ (i32.const 2)
+ )
+ (block
+ (i32.store
+ (global.get $__bysyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__bysyncify_data)
+ )
+ (i32.const -8)
+ )
+ )
+ (local.set $4
+ (i32.load
+ (global.get $__bysyncify_data)
+ )
+ )
+ (local.set $x
+ (i32.load
+ (local.get $4)
+ )
+ )
+ (local.set $1
+ (i32.load offset=4
+ (local.get $4)
+ )
+ )
+ )
+ )
+ (local.set $2
+ (block $__bysyncify_unwind (result i32)
+ (block
+ (block
+ (if
+ (i32.eq
+ (global.get $__bysyncify_state)
+ (i32.const 2)
+ )
+ (block
+ (i32.store
+ (global.get $__bysyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__bysyncify_data)
+ )
+ (i32.const -4)
+ )
+ )
+ (local.set $3
+ (i32.load
+ (i32.load
+ (global.get $__bysyncify_data)
+ )
+ )
+ )
+ )
+ )
+ (block
+ (if
+ (i32.or
+ (local.get $x)
+ (i32.eq
+ (global.get $__bysyncify_state)
+ (i32.const 2)
+ )
+ )
+ (if
+ (if (result i32)
+ (i32.eq
+ (global.get $__bysyncify_state)
+ (i32.const 0)
+ )
+ (i32.const 1)
+ (i32.eq
+ (local.get $3)
+ (i32.const 0)
+ )
+ )
+ (block
+ (call $import3
+ (i32.const 1)
+ )
+ (if
+ (i32.eq
+ (global.get $__bysyncify_state)
+ (i32.const 1)
+ )
+ (br $__bysyncify_unwind
+ (i32.const 0)
+ )
+ )
+ )
+ )
+ )
+ (if
+ (i32.or
+ (i32.eqz
+ (local.get $x)
+ )
+ (i32.eq
+ (global.get $__bysyncify_state)
+ (i32.const 2)
+ )
+ )
+ (if
+ (if (result i32)
+ (i32.eq
+ (global.get $__bysyncify_state)
+ (i32.const 0)
+ )
+ (i32.const 1)
+ (i32.eq
+ (local.get $3)
+ (i32.const 1)
+ )
+ )
+ (block
+ (call $import3
+ (i32.const 2)
+ )
+ (if
+ (i32.eq
+ (global.get $__bysyncify_state)
+ (i32.const 1)
+ )
+ (br $__bysyncify_unwind
+ (i32.const 1)
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ (return)
+ )
+ )
+ )
+ (block
+ (i32.store
+ (i32.load
+ (global.get $__bysyncify_data)
+ )
+ (local.get $2)
+ )
+ (i32.store
+ (global.get $__bysyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__bysyncify_data)
+ )
+ (i32.const 4)
+ )
+ )
+ )
+ (block
+ (local.set $5
+ (i32.load
+ (global.get $__bysyncify_data)
+ )
+ )
+ (i32.store
+ (local.get $5)
+ (local.get $x)
+ )
+ (i32.store offset=4
+ (local.get $5)
+ (local.get $1)
+ )
+ (i32.store
+ (global.get $__bysyncify_data)
+ (i32.add
+ (i32.load
+ (global.get $__bysyncify_data)
+ )
+ (i32.const 8)
+ )
+ )
+ )
+ )
+ (func $calls-indirect (; 6 ;) (type $FUNCSIG$vi) (param $x i32)
+ (local $1 i32)
+ (call_indirect (type $f)
+ (local.get $x)
+ )
+ )
+ (func $bysyncify_start_unwind (; 7 ;) (param $0 i32)
+ (if
+ (i32.gt_u
+ (i32.load
+ (local.get $0)
+ )
+ (i32.load offset=4
+ (local.get $0)
+ )
+ )
+ (unreachable)
+ )
+ (global.set $__bysyncify_state
+ (i32.const 1)
+ )
+ (global.set $__bysyncify_data
+ (local.get $0)
+ )
+ )
+ (func $bysyncify_stop_unwind (; 8 ;)
+ (global.set $__bysyncify_state
+ (i32.const 0)
+ )
+ )
+ (func $bysyncify_start_rewind (; 9 ;) (param $0 i32)
+ (if
+ (i32.gt_u
+ (i32.load
+ (local.get $0)
+ )
+ (i32.load offset=4
+ (local.get $0)
+ )
+ )
+ (unreachable)
+ )
+ (global.set $__bysyncify_state
+ (i32.const 2)
+ )
+ (global.set $__bysyncify_data
+ (local.get $0)
+ )
+ )
+ (func $bysyncify_stop_rewind (; 10 ;)
+ (global.set $__bysyncify_state
+ (i32.const 0)
+ )
+ )
+)
diff --git a/test/passes/bysyncify_pass-arg=bysyncify-ignore-indirect.wast b/test/passes/bysyncify_pass-arg=bysyncify-ignore-indirect.wast
new file mode 100644
index 000000000..33500baba
--- /dev/null
+++ b/test/passes/bysyncify_pass-arg=bysyncify-ignore-indirect.wast
@@ -0,0 +1,26 @@
+(module
+ (memory 1 2)
+ (type $f (func))
+ (import "env" "import" (func $import))
+ (import "env" "import2" (func $import2 (result i32)))
+ (import "env" "import3" (func $import3 (param i32)))
+ (table 1 1)
+ (func $calls-import
+ (call $import)
+ )
+ (func $calls-import2-drop
+ (drop (call $import2))
+ )
+ (func $calls-import2-if-else (param $x i32)
+ (if (local.get $x)
+ (call $import3 (i32.const 1))
+ (call $import3 (i32.const 2))
+ )
+ )
+ (func $calls-indirect (param $x i32)
+ (call_indirect (type $f)
+ (local.get $x)
+ )
+ )
+)
+
diff --git a/test/passes/bysyncify_pass-arg=bysyncify-imports@env.import,env.import2.txt b/test/passes/bysyncify_pass-arg=bysyncify-imports@env.import,env.import2.txt
index 81a37a643..b75e59d88 100644
--- a/test/passes/bysyncify_pass-arg=bysyncify-imports@env.import,env.import2.txt
+++ b/test/passes/bysyncify_pass-arg=bysyncify-imports@env.import,env.import2.txt
@@ -12,229 +12,29 @@
(func $do_sleep (; 0 ;) (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 $__bysyncify_state)
- (i32.const 2)
- )
- (block
- (i32.store
- (global.get $__bysyncify_data)
- (i32.add
- (i32.load
- (global.get $__bysyncify_data)
- )
- (i32.const -8)
- )
- )
- (local.set $4
- (i32.load
- (global.get $__bysyncify_data)
- )
- )
- (local.set $0
- (i32.load
- (local.get $4)
- )
- )
- (local.set $1
- (i32.load offset=4
- (local.get $4)
- )
- )
- )
+ (local.set $0
+ (global.get $sleeping)
)
- (local.set $2
- (block $__bysyncify_unwind (result i32)
- (block
- (block
- (if
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 2)
- )
- (block
- (i32.store
- (global.get $__bysyncify_data)
- (i32.add
- (i32.load
- (global.get $__bysyncify_data)
- )
- (i32.const -4)
- )
- )
- (local.set $3
- (i32.load
- (i32.load
- (global.get $__bysyncify_data)
- )
- )
- )
- )
- )
- (block
- (if
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 0)
- )
- (block
- (local.set $0
- (global.get $sleeping)
- )
- (local.set $1
- (i32.eqz
- (local.get $0)
- )
- )
- )
- )
- (nop)
- (block
- (if
- (i32.or
- (local.get $1)
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 2)
- )
- )
- (block
- (if
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 0)
- )
- (global.set $sleeping
- (i32.const 1)
- )
- )
- (if
- (if (result i32)
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 0)
- )
- (i32.const 1)
- (i32.eq
- (local.get $3)
- (i32.const 0)
- )
- )
- (block
- (call $bysyncify_start_unwind
- (i32.const 4)
- )
- (if
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 1)
- )
- (br $__bysyncify_unwind
- (i32.const 0)
- )
- )
- )
- )
- )
- )
- (if
- (i32.or
- (i32.eqz
- (local.get $1)
- )
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 2)
- )
- )
- (block
- (if
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 0)
- )
- (global.set $sleeping
- (i32.const 0)
- )
- )
- (if
- (if (result i32)
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 0)
- )
- (i32.const 1)
- (i32.eq
- (local.get $3)
- (i32.const 1)
- )
- )
- (block
- (call $bysyncify_stop_rewind)
- (if
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 1)
- )
- (br $__bysyncify_unwind
- (i32.const 1)
- )
- )
- )
- )
- )
- )
- )
- )
- )
- (return)
- )
+ (local.set $1
+ (i32.eqz
+ (local.get $0)
)
)
- (block
- (i32.store
- (i32.load
- (global.get $__bysyncify_data)
+ (if
+ (local.get $1)
+ (block
+ (global.set $sleeping
+ (i32.const 1)
)
- (local.get $2)
- )
- (i32.store
- (global.get $__bysyncify_data)
- (i32.add
- (i32.load
- (global.get $__bysyncify_data)
- )
+ (call $bysyncify_start_unwind
(i32.const 4)
)
)
- )
- (block
- (local.set $5
- (i32.load
- (global.get $__bysyncify_data)
- )
- )
- (i32.store
- (local.get $5)
- (local.get $0)
- )
- (i32.store offset=4
- (local.get $5)
- (local.get $1)
- )
- (i32.store
- (global.get $__bysyncify_data)
- (i32.add
- (i32.load
- (global.get $__bysyncify_data)
- )
- (i32.const 8)
+ (block
+ (global.set $sleeping
+ (i32.const 0)
)
+ (call $bysyncify_stop_rewind)
)
)
)
@@ -432,120 +232,10 @@
(nop)
)
(func $second_event (; 4 ;) (type $FUNCSIG$v)
- (local $0 i32)
- (local $1 i32)
- (if
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 2)
- )
- (nop)
+ (call $bysyncify_start_rewind
+ (i32.const 4)
)
- (local.set $0
- (block $__bysyncify_unwind (result i32)
- (block
- (block
- (if
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 2)
- )
- (block
- (i32.store
- (global.get $__bysyncify_data)
- (i32.add
- (i32.load
- (global.get $__bysyncify_data)
- )
- (i32.const -4)
- )
- )
- (local.set $1
- (i32.load
- (i32.load
- (global.get $__bysyncify_data)
- )
- )
- )
- )
- )
- (block
- (if
- (if (result i32)
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 0)
- )
- (i32.const 1)
- (i32.eq
- (local.get $1)
- (i32.const 0)
- )
- )
- (block
- (call $bysyncify_start_rewind
- (i32.const 4)
- )
- (if
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 1)
- )
- (br $__bysyncify_unwind
- (i32.const 0)
- )
- )
- )
- )
- (if
- (if (result i32)
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 0)
- )
- (i32.const 1)
- (i32.eq
- (local.get $1)
- (i32.const 1)
- )
- )
- (block
- (call $work)
- (if
- (i32.eq
- (global.get $__bysyncify_state)
- (i32.const 1)
- )
- (br $__bysyncify_unwind
- (i32.const 1)
- )
- )
- )
- )
- )
- )
- (return)
- )
- )
- )
- (block
- (i32.store
- (i32.load
- (global.get $__bysyncify_data)
- )
- (local.get $0)
- )
- (i32.store
- (global.get $__bysyncify_data)
- (i32.add
- (i32.load
- (global.get $__bysyncify_data)
- )
- (i32.const 4)
- )
- )
- )
- (nop)
+ (call $work)
)
(func $never_sleep (; 5 ;) (type $FUNCSIG$v)
(call $stuff)
diff --git a/test/unit/input/bysyncify-pure.txt b/test/unit/input/bysyncify-pure.txt
new file mode 100644
index 000000000..1f85db1f1
--- /dev/null
+++ b/test/unit/input/bysyncify-pure.txt
@@ -0,0 +1,17 @@
+(i32.const 100)
+(i32.const 10)
+(i32.const 1)
+(i32.const 20)
+(i32.const 1000)
+(i32.const 2000)
+(i32.const 4000)
+(i32.const 200)
+(i32.const 300)
+(i32.const 400)
+(i32.const 1000)
+(i32.const 3000)
+(i32.const 4000)
+(i32.const 30)
+(i32.const 2)
+(i32.const 40)
+(i32.const 500)
diff --git a/test/unit/input/bysyncify-pure.wast b/test/unit/input/bysyncify-pure.wast
new file mode 100644
index 000000000..b79bfe2b0
--- /dev/null
+++ b/test/unit/input/bysyncify-pure.wast
@@ -0,0 +1,59 @@
+(module
+ (memory 1 1)
+ (import "spectest" "print" (func $print (param i32)))
+ (import "bysyncify" "start_unwind" (func $bysyncify_start_unwind (param i32)))
+ (import "bysyncify" "stop_unwind" (func $bysyncify_stop_unwind))
+ (import "bysyncify" "start_rewind" (func $bysyncify_start_rewind (param i32)))
+ (import "bysyncify" "stop_rewind" (func $bysyncify_stop_rewind))
+ (global $sleeping (mut i32) (i32.const 0))
+ (start $runtime)
+ (func $main
+ (call $print (i32.const 10))
+ (call $before)
+ (call $print (i32.const 20))
+ (call $sleep)
+ (call $print (i32.const 30))
+ (call $after)
+ (call $print (i32.const 40))
+ )
+ (func $before
+ (call $print (i32.const 1))
+ )
+ (func $sleep
+ (call $print (i32.const 1000))
+ (if
+ (i32.eqz (global.get $sleeping))
+ (block
+ (call $print (i32.const 2000))
+ (global.set $sleeping (i32.const 1))
+ (i32.store (i32.const 16) (i32.const 24))
+ (i32.store (i32.const 20) (i32.const 1024))
+ (call $bysyncify_start_unwind (i32.const 16))
+ )
+ (block
+ (call $print (i32.const 3000))
+ (call $bysyncify_stop_rewind)
+ (global.set $sleeping (i32.const 0))
+ )
+ )
+ (call $print (i32.const 4000))
+ )
+ (func $after
+ (call $print (i32.const 2))
+ )
+ (func $runtime
+ (call $print (i32.const 100))
+ ;; call main the first time, let the stack unwind
+ (call $main)
+ (call $print (i32.const 200))
+ (call $bysyncify_stop_unwind)
+ (call $print (i32.const 300))
+ ;; ...can do some async stuff around here...
+ ;; set the rewind in motion
+ (call $bysyncify_start_rewind (i32.const 16))
+ (call $print (i32.const 400))
+ (call $main)
+ (call $print (i32.const 500))
+ )
+)
+
diff --git a/test/unit/test_bysyncify.py b/test/unit/test_bysyncify.py
index 29a8ae7cb..8d5a780e8 100644
--- a/test/unit/test_bysyncify.py
+++ b/test/unit/test_bysyncify.py
@@ -1,11 +1,11 @@
import os
-from scripts.test.shared import WASM_OPT, NODEJS, run_process
+from scripts.test.shared import WASM_OPT, WASM_DIS, WASM_SHELL, NODEJS, run_process
from utils import BinaryenTestCase
class BysyncifyTest(BinaryenTestCase):
- def test_bysyncify(self):
+ def test_bysyncify_js(self):
def test(args):
print(args)
run_process(WASM_OPT + args + [self.input_path('bysyncify-sleep.wast'), '--bysyncify', '-o', 'a.wasm'])
@@ -19,3 +19,10 @@ class BysyncifyTest(BinaryenTestCase):
test(['--optimize-level=1'])
test(['-O3'])
test(['-Os', '-g'])
+
+ def test_bysyncify_pure_wasm(self):
+ run_process(WASM_OPT + [self.input_path('bysyncify-pure.wast'), '--bysyncify', '-o', 'a.wasm'])
+ run_process(WASM_DIS + ['a.wasm', '-o', 'a.wast'])
+ output = run_process(WASM_SHELL + ['a.wast'], capture_output=True).stdout
+ with open(self.input_path('bysyncify-pure.txt')) as f:
+ self.assertEqual(f.read(), output)