126126// proper place, and the end to the proper end based on how much memory
127127// you reserved. Note that bysyncify will grow the stack up.
128128//
129- // The pass will also create three functions that let you control unwinding
129+ // The pass will also create four functions that let you control unwinding
130130// and rewinding:
131131//
132132// * bysyncify_start_unwind(data : i32): call this to start unwinding the
133133// stack from the current location. "data" must point to a data
134134// structure as described above (with fields containing valid data).
135135//
136+ // * bysyncify_stop_unwind(): call this to note that unwinding has
137+ // concluded. If no other code will run before you start to rewind,
138+ // this is not strictly necessary, however, if you swap between
139+ // coroutines, or even just want to run some normal code during a
140+ // "sleep", then you must call this at the proper time. Otherwise,
141+ // the code will think it is still unwinding when it should not be,
142+ // which means it will keep unwinding in a meaningless way.
143+ //
136144// * bysyncify_start_rewind(data : i32): call this to start rewinding the
137145// stack vack up to the location stored in the provided data. This prepares
138146// for the rewind; to start it, you must call the first function in the
141149// * bysyncify_stop_rewind(): call this to note that rewinding has
142150// concluded, and normal execution can resume.
143151//
144- // These three functions are exported so that you can call them from the
152+ // These four functions are exported so that you can call them from the
145153// outside. If you want to manage things from inside the wasm, then you
146154// couldn't have called them before they were created by this pass. To work
147155// around that, you can create imports to bysyncify.start_unwind,
148- // bysyncify.start_rewind, and bysyncify.stop_rewind; if those exist when
149- // this pass runs then it will turn those into direct calls to the functions
150- // that it creates.
156+ // bysyncify.stop_unwind, bysyncify. start_rewind, and bysyncify.stop_rewind;
157+ // if those exist when this pass runs then it will turn those into direct
158+ // calls to the functions that it creates.
151159//
152160// To use this API, call bysyncify_start_unwind when you want to. The call
153161// stack will then be unwound, and so execution will resume in the JS or
154- // other host environment on the outside that called into wasm. Then when
162+ // other host environment on the outside that called into wasm. When you
163+ // return there after unwinding, call bysyncify_stop_unwind. Then when
155164// you want to rewind, call bysyncify_start_rewind, and then call the same
156165// initial function you called before, so that unwinding can begin. The
157166// unwinding will reach the same function from which you started, since
163172// To customize this, you can provide an argument to wasm-opt (or another
164173// tool that can run this pass),
165174//
166- // --pass-arg=bysyncify@module1.base1,module2.base2,module3.base3
175+ // --pass-arg=bysyncify-imports @module1.base1,module2.base2,module3.base3
167176//
168177// Each module.base in that comma-separated list will be considered to
169178// be an import that can unwind/rewind, and all others are assumed not to
170179// (aside from the bysyncify.* imports, which are always assumed to). To
171180// say that no import (aside from bysyncify.*) can do so (that is, the
172181// opposite of the default behavior), you can simply provide an import
173- // that doesn't exist (say,
174- // --pass-arg=bysyncify@no.imports
182+ // that doesn't exist, for example:
183+
184+ // --pass-arg=bysyncify-imports@no.imports
175185//
176186
177187#include " ir/effects.h"
@@ -190,11 +200,13 @@ namespace {
190200static const Name BYSYNCIFY_STATE = " __bysyncify_state" ;
191201static const Name BYSYNCIFY_DATA = " __bysyncify_data" ;
192202static const Name BYSYNCIFY_START_UNWIND = " bysyncify_start_unwind" ;
203+ static const Name BYSYNCIFY_STOP_UNWIND = " bysyncify_stop_unwind" ;
193204static const Name BYSYNCIFY_START_REWIND = " bysyncify_start_rewind" ;
194205static const Name BYSYNCIFY_STOP_REWIND = " bysyncify_stop_rewind" ;
195206static const Name BYSYNCIFY_UNWIND = " __bysyncify_unwind" ;
196207static const Name BYSYNCIFY = " bysyncify" ;
197208static const Name START_UNWIND = " start_unwind" ;
209+ static const Name STOP_UNWIND = " stop_unwind" ;
198210static const Name START_REWIND = " start_rewind" ;
199211static const Name STOP_REWIND = " stop_rewind" ;
200212static const Name BYSYNCIFY_GET_CALL_INDEX = " __bysyncify_get_call_index" ;
@@ -234,10 +246,10 @@ class ModuleAnalyzer {
234246 ModuleUtils::ParallelFunctionMap<Info> scanner (
235247 module , [&](Function* func, Info& info) {
236248 if (func->imported ()) {
237- // The bysyncify imports to start an unwind or rewind can definitely
238- // change the state.
249+ // The bysyncify imports can definitely change the state.
239250 if (func->module == BYSYNCIFY &&
240- (func->base == START_UNWIND || func->base == START_REWIND)) {
251+ (func->base == START_UNWIND || func->base == STOP_UNWIND ||
252+ func->base == START_REWIND || func->base == STOP_REWIND)) {
241253 info.canChangeState = true ;
242254 } else {
243255 info.canChangeState =
@@ -252,6 +264,8 @@ class ModuleAnalyzer {
252264 // Redirect the imports to the functions we'll add later.
253265 if (target->base == START_UNWIND) {
254266 curr->target = BYSYNCIFY_START_UNWIND;
267+ } else if (target->base == STOP_UNWIND) {
268+ curr->target = BYSYNCIFY_STOP_UNWIND;
255269 } else if (target->base == START_REWIND) {
256270 curr->target = BYSYNCIFY_START_REWIND;
257271 } else if (target->base == STOP_REWIND) {
@@ -328,7 +342,9 @@ class ModuleAnalyzer {
328342 // We only implement these at the very end, but we know that they
329343 // definitely change the state.
330344 if (curr->target == BYSYNCIFY_START_UNWIND ||
345+ curr->target == BYSYNCIFY_STOP_UNWIND ||
331346 curr->target == BYSYNCIFY_START_REWIND ||
347+ curr->target == BYSYNCIFY_STOP_REWIND ||
332348 curr->target == BYSYNCIFY_GET_CALL_INDEX ||
333349 curr->target == BYSYNCIFY_CHECK_CALL_INDEX) {
334350 canChangeState = true ;
@@ -775,7 +791,7 @@ struct Bysyncify : public Pass {
775791 // Find which imports can change the state.
776792 const char * ALL_IMPORTS_CAN_CHANGE_STATE = " __bysyncify_all_imports" ;
777793 auto stateChangingImports = runner->options .getArgumentOrDefault (
778- " bysyncify" , ALL_IMPORTS_CAN_CHANGE_STATE);
794+ " bysyncify-imports " , ALL_IMPORTS_CAN_CHANGE_STATE);
779795 bool allImportsCanChangeState =
780796 stateChangingImports == ALL_IMPORTS_CAN_CHANGE_STATE;
781797 std::string separator = " ," ;
@@ -895,6 +911,7 @@ struct Bysyncify : public Pass {
895911 true ,
896912 State::Unwinding,
897913 builder.makeGlobalSet (BYSYNCIFY_DATA, builder.makeLocalGet (0 , i32 )));
914+ makeFunction (BYSYNCIFY_STOP_UNWIND, false , State::Normal, nullptr );
898915 makeFunction (
899916 BYSYNCIFY_START_REWIND,
900917 true ,
0 commit comments