Skip to content

Commit 3d3a5a6

Browse files
authored
Bysyncify: bysyncify_stop_unwind (#2173)
Add a method to note the stopping of an unwind. This is enough to implement coroutines. Includes an example of coroutine usage in the test suite.
1 parent 1cd34c2 commit 3d3a5a6

10 files changed

Lines changed: 439 additions & 165 deletions

src/passes/Bysyncify.cpp

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,21 @@
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
@@ -141,17 +149,18 @@
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
@@ -163,15 +172,16 @@
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 {
190200
static const Name BYSYNCIFY_STATE = "__bysyncify_state";
191201
static const Name BYSYNCIFY_DATA = "__bysyncify_data";
192202
static const Name BYSYNCIFY_START_UNWIND = "bysyncify_start_unwind";
203+
static const Name BYSYNCIFY_STOP_UNWIND = "bysyncify_stop_unwind";
193204
static const Name BYSYNCIFY_START_REWIND = "bysyncify_start_rewind";
194205
static const Name BYSYNCIFY_STOP_REWIND = "bysyncify_stop_rewind";
195206
static const Name BYSYNCIFY_UNWIND = "__bysyncify_unwind";
196207
static const Name BYSYNCIFY = "bysyncify";
197208
static const Name START_UNWIND = "start_unwind";
209+
static const Name STOP_UNWIND = "stop_unwind";
198210
static const Name START_REWIND = "start_rewind";
199211
static const Name STOP_REWIND = "stop_rewind";
200212
static 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,

test/passes/bysyncify.txt

Lines changed: 75 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
(global $__bysyncify_state (mut i32) (i32.const 0))
77
(global $__bysyncify_data (mut i32) (i32.const 0))
88
(export "bysyncify_start_unwind" (func $bysyncify_start_unwind))
9+
(export "bysyncify_stop_unwind" (func $bysyncify_stop_unwind))
910
(export "bysyncify_start_rewind" (func $bysyncify_start_rewind))
1011
(export "bysyncify_stop_rewind" (func $bysyncify_stop_rewind))
1112
(func $do_sleep (; 0 ;) (type $FUNCSIG$v)
@@ -151,16 +152,40 @@
151152
(i32.const 2)
152153
)
153154
)
154-
(if
155-
(i32.eq
156-
(global.get $__bysyncify_state)
157-
(i32.const 0)
158-
)
159-
(block
155+
(block
156+
(if
157+
(i32.eq
158+
(global.get $__bysyncify_state)
159+
(i32.const 0)
160+
)
160161
(global.set $sleeping
161162
(i32.const 0)
162163
)
163-
(call $bysyncify_stop_rewind)
164+
)
165+
(if
166+
(if (result i32)
167+
(i32.eq
168+
(global.get $__bysyncify_state)
169+
(i32.const 0)
170+
)
171+
(i32.const 1)
172+
(i32.eq
173+
(local.get $3)
174+
(i32.const 1)
175+
)
176+
)
177+
(block
178+
(call $bysyncify_stop_rewind)
179+
(if
180+
(i32.eq
181+
(global.get $__bysyncify_state)
182+
(i32.const 1)
183+
)
184+
(br $__bysyncify_unwind
185+
(i32.const 1)
186+
)
187+
)
188+
)
164189
)
165190
)
166191
)
@@ -457,6 +482,31 @@
457482
(i32.const 0)
458483
)
459484
)
485+
(block
486+
(call $bysyncify_stop_unwind)
487+
(if
488+
(i32.eq
489+
(global.get $__bysyncify_state)
490+
(i32.const 1)
491+
)
492+
(br $__bysyncify_unwind
493+
(i32.const 0)
494+
)
495+
)
496+
)
497+
)
498+
(if
499+
(if (result i32)
500+
(i32.eq
501+
(global.get $__bysyncify_state)
502+
(i32.const 0)
503+
)
504+
(i32.const 1)
505+
(i32.eq
506+
(local.get $1)
507+
(i32.const 1)
508+
)
509+
)
460510
(block
461511
(call $bysyncify_start_rewind
462512
(i32.const 4)
@@ -467,7 +517,7 @@
467517
(i32.const 1)
468518
)
469519
(br $__bysyncify_unwind
470-
(i32.const 0)
520+
(i32.const 1)
471521
)
472522
)
473523
)
@@ -481,7 +531,7 @@
481531
(i32.const 1)
482532
(i32.eq
483533
(local.get $1)
484-
(i32.const 1)
534+
(i32.const 2)
485535
)
486536
)
487537
(block
@@ -492,7 +542,7 @@
492542
(i32.const 1)
493543
)
494544
(br $__bysyncify_unwind
495-
(i32.const 1)
545+
(i32.const 2)
496546
)
497547
)
498548
)
@@ -546,7 +596,12 @@
546596
(local.get $0)
547597
)
548598
)
549-
(func $bysyncify_start_rewind (; 7 ;) (param $0 i32)
599+
(func $bysyncify_stop_unwind (; 7 ;)
600+
(global.set $__bysyncify_state
601+
(i32.const 0)
602+
)
603+
)
604+
(func $bysyncify_start_rewind (; 8 ;) (param $0 i32)
550605
(if
551606
(i32.gt_u
552607
(i32.load
@@ -565,7 +620,7 @@
565620
(local.get $0)
566621
)
567622
)
568-
(func $bysyncify_stop_rewind (; 8 ;)
623+
(func $bysyncify_stop_rewind (; 9 ;)
569624
(global.set $__bysyncify_state
570625
(i32.const 0)
571626
)
@@ -583,6 +638,7 @@
583638
(global $__bysyncify_state (mut i32) (i32.const 0))
584639
(global $__bysyncify_data (mut i32) (i32.const 0))
585640
(export "bysyncify_start_unwind" (func $bysyncify_start_unwind))
641+
(export "bysyncify_stop_unwind" (func $bysyncify_stop_unwind))
586642
(export "bysyncify_start_rewind" (func $bysyncify_start_rewind))
587643
(export "bysyncify_stop_rewind" (func $bysyncify_stop_rewind))
588644
(func $calls-import (; 3 ;) (type $FUNCSIG$v)
@@ -2683,7 +2739,12 @@
26832739
(local.get $0)
26842740
)
26852741
)
2686-
(func $bysyncify_start_rewind (; 20 ;) (param $0 i32)
2742+
(func $bysyncify_stop_unwind (; 20 ;)
2743+
(global.set $__bysyncify_state
2744+
(i32.const 0)
2745+
)
2746+
)
2747+
(func $bysyncify_start_rewind (; 21 ;) (param $0 i32)
26872748
(if
26882749
(i32.gt_u
26892750
(i32.load
@@ -2702,7 +2763,7 @@
27022763
(local.get $0)
27032764
)
27042765
)
2705-
(func $bysyncify_stop_rewind (; 21 ;)
2766+
(func $bysyncify_stop_rewind (; 22 ;)
27062767
(global.set $__bysyncify_state
27072768
(i32.const 0)
27082769
)

test/passes/bysyncify.wast

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
(module
33
(memory 1 2)
44
(import "bysyncify" "start_unwind" (func $bysyncify_start_unwind (param i32)))
5+
(import "bysyncify" "stop_unwind" (func $bysyncify_stop_unwind))
56
(import "bysyncify" "start_rewind" (func $bysyncify_start_rewind (param i32)))
67
(import "bysyncify" "stop_rewind" (func $bysyncify_stop_rewind))
78
(global $sleeping (mut i32) (i32.const 0))
@@ -34,8 +35,10 @@
3435
;; work will sleep, so we exit through here while it is paused
3536
)
3637
;; the second event called from the main event loop: to resume $work,
37-
;; initiate a rewind, and then do the call to start things back up
38+
;; stop the unwind, then prepare a rewind, and initiate it by doing
39+
;; the call to rewind the call stack back up to where it was
3840
(func $second_event
41+
(call $bysyncify_stop_unwind)
3942
(call $bysyncify_start_rewind (i32.const 4))
4043
(call $work)
4144
)

test/passes/bysyncify_optimize-level=1.txt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
(global $__bysyncify_state (mut i32) (i32.const 0))
1111
(global $__bysyncify_data (mut i32) (i32.const 0))
1212
(export "bysyncify_start_unwind" (func $bysyncify_start_unwind))
13+
(export "bysyncify_stop_unwind" (func $bysyncify_stop_unwind))
1314
(export "bysyncify_start_rewind" (func $bysyncify_start_rewind))
1415
(export "bysyncify_stop_rewind" (func $bysyncify_stop_rewind))
1516
(func $calls-import (; 3 ;) (type $FUNCSIG$v)
@@ -1514,7 +1515,12 @@
15141515
(local.get $0)
15151516
)
15161517
)
1517-
(func $bysyncify_start_rewind (; 20 ;) (param $0 i32)
1518+
(func $bysyncify_stop_unwind (; 20 ;)
1519+
(global.set $__bysyncify_state
1520+
(i32.const 0)
1521+
)
1522+
)
1523+
(func $bysyncify_start_rewind (; 21 ;) (param $0 i32)
15181524
(if
15191525
(i32.gt_u
15201526
(i32.load
@@ -1533,7 +1539,7 @@
15331539
(local.get $0)
15341540
)
15351541
)
1536-
(func $bysyncify_stop_rewind (; 21 ;)
1542+
(func $bysyncify_stop_rewind (; 22 ;)
15371543
(global.set $__bysyncify_state
15381544
(i32.const 0)
15391545
)

0 commit comments

Comments
 (0)