Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mark asyncify exports #2327

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/passes/Asyncify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,8 @@ class ModuleAnalyzer {
return info.canChangeState && !info.isTopMostRuntime;
}

bool canChangeState(Function* func) { return map[func].canChangeState; }

bool canChangeState(Expression* curr) {
// Look inside to see if we call any of the things we know can change the
// state.
Expand Down Expand Up @@ -1150,6 +1152,32 @@ struct Asyncify : public Pass {
runner.setValidateGlobally(false);
runner.run();
}
// Mark exported asyncified functions.
//
// This creates a custom section for each function that:
// 1) Is exported and, thus, directly accessible by user.
// 2) Might directly or indirectly start an async operation (and unwind).
//
// Each such section will have a name "asyncify" and name of the export as
// its data.
//
// This gives the JavaScript side an opportunity to wrap functions into an
// `async` variant only if it's necessary, and leave others intact.
auto& userSections = module->userSections;
for (auto& exp : module->exports) {
if (exp->kind != ExternalKind::Function) {
continue;
}
auto* func = module->getFunction(exp->value);
if (!analyzer.canChangeState(func)) {
continue;
}
userSections.resize(userSections.size() + 1);
auto& section = userSections.back();
section.name = BinaryConsts::UserSections::Asyncify;
auto& name = exp->name;
section.data.assign(name.c_str(), name.c_str() + name.size());
}
// Finally, add function support (that should not have been seen by
// the previous passes).
addFunctions(module);
Expand Down
19 changes: 19 additions & 0 deletions src/passes/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2122,6 +2122,25 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
doIndent(o, indent);
o << ";; custom section \"" << section.name << "\", size "
<< section.data.size();
bool isPrintable = true;
for (auto c : section.data) {
if (!isprint(c)) {
isPrintable = false;
break;
}
}
if (isPrintable) {
o << ", contents: ";
// std::quoted is not available in all the supported compilers yet.
o << '"';
for (auto c : section.data) {
if (c == '\\' || c == '"') {
o << '\\';
}
o << c;
}
o << '"';
}
o << maybeNewLine;
}
decIndent();
Expand Down
1 change: 1 addition & 0 deletions src/wasm-binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ extern const char* Dylink;
extern const char* Linking;
extern const char* Producers;
extern const char* TargetFeatures;
extern const char* Asyncify;

extern const char* AtomicsFeature;
extern const char* BulkMemoryFeature;
Expand Down
1 change: 1 addition & 0 deletions src/wasm/wasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const char* Dylink = "dylink";
const char* Linking = "linking";
const char* Producers = "producers";
const char* TargetFeatures = "target_features";
const char* Asyncify = "asyncify";
const char* AtomicsFeature = "atomics";
const char* BulkMemoryFeature = "bulk-memory";
const char* ExceptionHandlingFeature = "exception-handling";
Expand Down
2 changes: 1 addition & 1 deletion test/fib-dbg.wasm.fromBinary
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,6 @@
(local $0 i32)
(nop)
)
;; custom section "sourceMappingURL", size 35
;; custom section "sourceMappingURL", size 35, contents: "\"http://localhost:8000/fib.wasm.map"
)

38 changes: 38 additions & 0 deletions test/passes/asyncify.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
(global $sleeping (mut i32) (i32.const 0))
(global $__asyncify_state (mut i32) (i32.const 0))
(global $__asyncify_data (mut i32) (i32.const 0))
(export "do_sleep" (func $do_sleep))
(export "work" (func $work))
(export "stuff" (func $stuff))
(export "first_event" (func $first_event))
(export "second_event" (func $second_event))
(export "never_sleep" (func $never_sleep))
(export "asyncify_start_unwind" (func $asyncify_start_unwind))
(export "asyncify_stop_unwind" (func $asyncify_stop_unwind))
(export "asyncify_start_rewind" (func $asyncify_start_rewind))
Expand Down Expand Up @@ -366,6 +372,9 @@
(unreachable)
)
)
;; custom section "asyncify", size 8, contents: "do_sleep"
;; custom section "asyncify", size 4, contents: "work"
;; custom section "asyncify", size 11, contents: "first_event"
)
(module
(type $FUNCSIG$v (func))
Expand All @@ -378,6 +387,22 @@
(memory $0 1 2)
(global $__asyncify_state (mut i32) (i32.const 0))
(global $__asyncify_data (mut i32) (i32.const 0))
(export "calls-import" (func $calls-import))
(export "calls-import2" (func $calls-import2))
(export "calls-import2-drop" (func $calls-import2-drop))
(export "calls-nothing" (func $calls-nothing))
(export "many-locals" (func $many-locals))
(export "calls-import2-if" (func $calls-import2-if))
(export "alls-import2-if-else" (func $calls-import2-if-else))
(export "calls-import2-if-else-oneside" (func $calls-import2-if-else-oneside))
(export "calls-import2-if-else-oneside2" (func $calls-import2-if-else-oneside2))
(export "calls-loop" (func $calls-loop))
(export "calls-loop2" (func $calls-loop2))
(export "calls-mix" (func $calls-mix))
(export "boring" (func $boring))
(export "calls-mix-deep" (func $calls-mix-deep))
(export "boring-deep" (func $boring-deep))
(export "import-deep" (func $import-deep))
(export "asyncify_start_unwind" (func $asyncify_start_unwind))
(export "asyncify_stop_unwind" (func $asyncify_stop_unwind))
(export "asyncify_start_rewind" (func $asyncify_start_rewind))
Expand Down Expand Up @@ -2899,6 +2924,19 @@
(unreachable)
)
)
;; custom section "asyncify", size 12, contents: "calls-import"
;; custom section "asyncify", size 13, contents: "calls-import2"
;; custom section "asyncify", size 18, contents: "calls-import2-drop"
;; custom section "asyncify", size 11, contents: "many-locals"
;; custom section "asyncify", size 16, contents: "calls-import2-if"
;; custom section "asyncify", size 20, contents: "alls-import2-if-else"
;; custom section "asyncify", size 29, contents: "calls-import2-if-else-oneside"
;; custom section "asyncify", size 30, contents: "calls-import2-if-else-oneside2"
;; custom section "asyncify", size 10, contents: "calls-loop"
;; custom section "asyncify", size 11, contents: "calls-loop2"
;; custom section "asyncify", size 9, contents: "calls-mix"
;; custom section "asyncify", size 14, contents: "calls-mix-deep"
;; custom section "asyncify", size 11, contents: "import-deep"
)
(module
(memory $0 1 1)
Expand Down
46 changes: 23 additions & 23 deletions test/passes/asyncify.wast
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
(global $sleeping (mut i32) (i32.const 0))
;; do a sleep operation: start a sleep if running, or resume after a sleep
;; if we just rewound.
(func $do_sleep
(func $do_sleep (export "do_sleep")
(if
(i32.eqz (global.get $sleeping))
(block
Expand All @@ -23,27 +23,27 @@
)
)
;; a function that does some work and has a sleep (async pause/resume) in the middle
(func $work
(func $work (export "work")
(call $stuff) ;; do some work
(call $do_sleep) ;; take a break
(call $stuff) ;; do some more work
)
(func $stuff)
(func $stuff (export "stuff"))
;; the first event called from the main event loop: just call into $work
(func $first_event
(func $first_event (export "first_event")
(call $work)
;; work will sleep, so we exit through here while it is paused
)
;; the second event called from the main event loop: to resume $work,
;; stop the unwind, then prepare a rewind, and initiate it by doing
;; stop the unwind, then prepare a rewind, and initiate it by doing
;; the call to rewind the call stack back up to where it was
(func $second_event
(func $second_event (export "second_event")
(call $asyncify_stop_unwind)
(call $asyncify_start_rewind (i32.const 4))
(call $work)
)
;; a function that can't do a sleep
(func $never_sleep
(func $never_sleep (export "never_sleep")
(call $stuff)
(call $stuff)
(call $stuff)
Expand All @@ -55,21 +55,21 @@
(import "env" "import" (func $import))
(import "env" "import2" (func $import2 (result i32)))
(import "env" "import3" (func $import3 (param i32)))
(func $calls-import
(func $calls-import (export "calls-import")
(call $import)
)
(func $calls-import2 (result i32)
(func $calls-import2 (export "calls-import2") (result i32)
(local $temp i32)
(local.set $temp (call $import2))
(return (local.get $temp))
)
(func $calls-import2-drop
(func $calls-import2-drop (export "calls-import2-drop")
(drop (call $import2))
)
(func $calls-nothing
(func $calls-nothing (export "calls-nothing")
(drop (i32.eqz (i32.const 17)))
)
(func $many-locals (param $x i32) (result i32)
(func $many-locals (export "many-locals") (param $x i32) (result i32)
(local $y i32)
(loop $l
(local.set $x
Expand All @@ -83,32 +83,32 @@
(call $import)
(return (local.get $y))
)
(func $calls-import2-if (param $x i32)
(func $calls-import2-if (export "calls-import2-if") (param $x i32)
(if (local.get $x)
(call $import)
)
)
(func $calls-import2-if-else (param $x i32)
(func $calls-import2-if-else (export "alls-import2-if-else") (param $x i32)
(if (local.get $x)
(call $import3 (i32.const 1))
(call $import3 (i32.const 2))
)
)
(func $calls-import2-if-else-oneside (param $x i32) (result i32)
(func $calls-import2-if-else-oneside (export "calls-import2-if-else-oneside") (param $x i32) (result i32)
(if (local.get $x)
(return (i32.const 1))
(call $import3 (i32.const 2))
)
(return (i32.const 3))
)
(func $calls-import2-if-else-oneside2 (param $x i32) (result i32)
(func $calls-import2-if-else-oneside2 (export "calls-import2-if-else-oneside2") (param $x i32) (result i32)
(if (local.get $x)
(call $import3 (i32.const 1))
(return (i32.const 2))
)
(return (i32.const 3))
)
(func $calls-loop (param $x i32)
(func $calls-loop (export "calls-loop") (param $x i32)
(loop $l
(call $import3 (i32.const 1))
(local.set $x
Expand All @@ -119,30 +119,30 @@
)
)
)
(func $calls-loop2
(func $calls-loop2 (export "calls-loop2")
(loop $l
(br_if $l
(call $import2)
)
)
)
(func $calls-mix
(func $calls-mix (export "calls-mix")
(call $boring)
(call $import)
(call $boring)
(call $import)
)
(func $boring)
(func $calls-mix-deep
(func $boring (export "boring"))
(func $calls-mix-deep (export "calls-mix-deep")
(call $boring-deep)
(call $import-deep)
(call $boring)
(call $import)
)
(func $boring-deep
(func $boring-deep (export "boring-deep")
(call $boring)
)
(func $import-deep
(func $import-deep (export "import-deep")
(call $import)
)
)
Expand Down