Skip to content

Commit 1cd34c2

Browse files
authored
Bysyncify: async transform for wasm (#2172)
This adds a new pass, Bysyncify, which transforms code to allow unwind and rewinding the call stack and local state. This allows things like coroutines, turning synchronous code asynchronous, etc. The new pass file itself has a large comment on top with docs. So far the tests here seem to show this works, but this hasn't been tested heavily yet. My next step is to hook this up to emscripten as a replacement for asyncify/emterpreter, see emscripten-core/emscripten#8561 Note that this is completely usable by itself, so it could be useful for any language that needs coroutines etc., and not just ones using LLVM and/or emscripten. See docs on the ABI in the pass source.
1 parent 22ba241 commit 1cd34c2

19 files changed

Lines changed: 7995 additions & 5 deletions

build-js.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ mkdir -p ${OUT}
9494
$BINARYEN_SRC/passes/pass.cpp \
9595
$BINARYEN_SRC/passes/AlignmentLowering.cpp \
9696
$BINARYEN_SRC/passes/AvoidReinterprets.cpp \
97+
$BINARYEN_SRC/passes/Bysyncify.cpp \
9798
$BINARYEN_SRC/passes/CoalesceLocals.cpp \
9899
$BINARYEN_SRC/passes/DeadArgumentElimination.cpp \
99100
$BINARYEN_SRC/passes/CodeFolding.cpp \

src/ir/effects.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ namespace wasm {
2727

2828
struct EffectAnalyzer
2929
: public PostWalker<EffectAnalyzer, OverriddenVisitor<EffectAnalyzer>> {
30-
EffectAnalyzer(PassOptions& passOptions, Expression* ast = nullptr) {
30+
EffectAnalyzer(const PassOptions& passOptions, Expression* ast = nullptr) {
3131
ignoreImplicitTraps = passOptions.ignoreImplicitTraps;
3232
debugInfo = passOptions.debugInfo;
3333
if (ast) {
@@ -372,7 +372,7 @@ struct EffectAnalyzer
372372
// Helpers
373373

374374
static bool
375-
canReorder(PassOptions& passOptions, Expression* a, Expression* b) {
375+
canReorder(const PassOptions& passOptions, Expression* a, Expression* b) {
376376
EffectAnalyzer aEffects(passOptions, a);
377377
EffectAnalyzer bEffects(passOptions, b);
378378
return !aEffects.invalidates(bEffects);

src/ir/module-utils.h

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "ir/find_all.h"
2121
#include "ir/manipulation.h"
22+
#include "pass.h"
2223
#include "wasm.h"
2324

2425
namespace wasm {
@@ -287,6 +288,62 @@ template<typename T> inline void iterDefinedEvents(Module& wasm, T visitor) {
287288
}
288289
}
289290

291+
// Performs a parallel map on function in the module, emitting a map object
292+
// of function => result.
293+
// TODO: use in inlining and elsewhere
294+
template<typename T> struct ParallelFunctionMap {
295+
296+
typedef std::map<Function*, T> Map;
297+
Map map;
298+
299+
typedef std::function<void(Function*, T&)> Func;
300+
301+
struct Info {
302+
Map* map;
303+
Func work;
304+
};
305+
306+
ParallelFunctionMap(Module& wasm, Func work) {
307+
// Fill in map, as we operate on it in parallel (each function to its own
308+
// entry).
309+
for (auto& func : wasm.functions) {
310+
map[func.get()];
311+
}
312+
313+
// Run on the imports first. TODO: parallelize this too
314+
for (auto& func : wasm.functions) {
315+
if (func->imported()) {
316+
work(func.get(), map[func.get()]);
317+
}
318+
}
319+
320+
// Run on the implemented functions.
321+
struct Mapper : public WalkerPass<PostWalker<Mapper>> {
322+
323+
bool isFunctionParallel() override { return true; }
324+
325+
Mapper(Info* info) : info(info) {}
326+
327+
Mapper* create() override { return new Mapper(info); }
328+
329+
void doWalkFunction(Function* curr) {
330+
assert((*info->map).count(curr));
331+
info->work(curr, (*info->map)[curr]);
332+
}
333+
334+
private:
335+
Info* info;
336+
};
337+
338+
Info info = {&map, work};
339+
340+
PassRunner runner(&wasm);
341+
runner.setIsNested(true);
342+
runner.add<Mapper>(&info);
343+
runner.run();
344+
}
345+
};
346+
290347
} // namespace ModuleUtils
291348

292349
} // namespace wasm

src/pass.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,13 @@ struct PassOptions {
127127
}
128128
return arguments[key];
129129
}
130+
131+
std::string getArgumentOrDefault(std::string key, std::string default_) {
132+
if (arguments.count(key) == 0) {
133+
return default_;
134+
}
135+
return arguments[key];
136+
}
130137
};
131138

132139
//

0 commit comments

Comments
 (0)