diff options
-rw-r--r-- | src/passes/Bysyncify.cpp | 64 | ||||
-rw-r--r-- | test/passes/bysyncify.txt | 372 | ||||
-rw-r--r-- | test/passes/bysyncify_pass-arg=bysyncify-ignore-indirect.txt | 478 | ||||
-rw-r--r-- | test/passes/bysyncify_pass-arg=bysyncify-ignore-indirect.wast | 26 | ||||
-rw-r--r-- | test/passes/bysyncify_pass-arg=bysyncify-imports@env.import,env.import2.txt | 346 | ||||
-rw-r--r-- | test/unit/input/bysyncify-pure.txt | 17 | ||||
-rw-r--r-- | test/unit/input/bysyncify-pure.wast | 59 | ||||
-rw-r--r-- | test/unit/test_bysyncify.py | 11 |
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 = ↦ 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) |