summaryrefslogtreecommitdiff
path: root/src/passes/Bysyncify.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/passes/Bysyncify.cpp')
-rw-r--r--src/passes/Bysyncify.cpp91
1 files changed, 59 insertions, 32 deletions
diff --git a/src/passes/Bysyncify.cpp b/src/passes/Bysyncify.cpp
index f9f679f2e..dcc543c5e 100644
--- a/src/passes/Bysyncify.cpp
+++ b/src/passes/Bysyncify.cpp
@@ -167,21 +167,30 @@
// we are recreating the call stack. At that point you should call
// bysyncify_stop_rewind and then execution can resume normally.
//
-// By default this pass assumes that any import may call any of the
-// exported bysyncify methods, that is, any import may start an unwind/rewind.
-// To customize this, you can provide an argument to wasm-opt (or another
-// tool that can run this pass),
+// By default this pass is very carefuly: it assumes that any import and
+// any indirect call may start an unwind/rewind operation. If you know more
+// specific information you can inform bysyncify about that, which can reduce
+// a great deal of overhead, as it can instrument less code. The relevant
+// options to wasm-opt etc. are:
//
// --pass-arg=bysyncify-imports@module1.base1,module2.base2,module3.base3
//
-// Each module.base in that comma-separated list will be considered to
-// be an import that can unwind/rewind, and all others are assumed not to
-// (aside from the bysyncify.* imports, which are always assumed to). To
-// say that no import (aside from bysyncify.*) can do so (that is, the
-// opposite of the default behavior), you can simply provide an import
-// that doesn't exist, for example:
-
-// --pass-arg=bysyncify-imports@no.imports
+// Each module.base in that comma-separated list will be considered to
+// be an import that can unwind/rewind, and all others are assumed not to
+// (aside from the bysyncify.* imports, which are always assumed to).
+//
+// --pass-arg=bysyncify-ignore-imports
+//
+// Ignore all imports (except for bynsyncify.*), that is, assume none of
+// them can start an unwind/rewind. (This is effectively the same as
+// providing bysyncify-imports with a list of non-existent imports.)
+//
+// --pass-arg=bysyncify-ignore-indirect
+//
+// Ignore all indirect calls. This implies that you know an call stack
+// will never need to be unwound with an indirect call somewhere in it.
+// If that is true for your codebase, then this can be extremely useful
+// as otherwise it looks like any indirect call can go to a lot of places.
//
#include "ir/effects.h"
@@ -225,6 +234,7 @@ const auto STACK_ALIGN = 4;
// by it.
class ModuleAnalyzer {
Module& module;
+ bool canIndirectChangeState;
struct Info {
bool canChangeState = false;
@@ -237,8 +247,9 @@ class ModuleAnalyzer {
public:
ModuleAnalyzer(Module& module,
- std::function<bool(Name, Name)> canImportChangeState)
- : module(module) {
+ std::function<bool(Name, Name)> canImportChangeState,
+ bool canIndirectChangeState)
+ : module(module), canIndirectChangeState(canIndirectChangeState) {
// Scan to see which functions can directly change the state.
// Also handle the bysyncify imports, removing them (as we will implement
// them later), and replace calls to them with calls to the later proper
@@ -281,15 +292,19 @@ public:
info->callsTo.insert(target);
}
void visitCallIndirect(CallIndirect* curr) {
- // TODO optimize
- info->canChangeState = true;
+ if (canIndirectChangeState) {
+ info->canChangeState = true;
+ }
+ // TODO optimize the other case, at least by type
}
Info* info;
Module* module;
+ bool canIndirectChangeState;
};
Walker walker;
walker.info = &info;
walker.module = &module;
+ walker.canIndirectChangeState = canIndirectChangeState;
walker.walk(func->body);
});
map.swap(scanner.map);
@@ -357,16 +372,20 @@ public:
}
}
void visitCallIndirect(CallIndirect* curr) {
- // TODO optimize
- canChangeState = true;
+ if (canIndirectChangeState) {
+ canChangeState = true;
+ }
+ // TODO optimize the other case, at least by type
}
Module* module;
Map* map;
+ bool canIndirectChangeState;
bool canChangeState = false;
};
Walker walker;
walker.module = &module;
walker.map = &map;
+ walker.canIndirectChangeState = canIndirectChangeState;
walker.walk(curr);
return walker.canChangeState;
}
@@ -788,23 +807,31 @@ private:
struct Bysyncify : public Pass {
void run(PassRunner* runner, Module* module) override {
bool optimize = runner->options.optimizeLevel > 0;
- // Find which imports can change the state.
- const char* ALL_IMPORTS_CAN_CHANGE_STATE = "__bysyncify_all_imports";
- auto stateChangingImports = runner->options.getArgumentOrDefault(
- "bysyncify-imports", ALL_IMPORTS_CAN_CHANGE_STATE);
- bool allImportsCanChangeState =
- stateChangingImports == ALL_IMPORTS_CAN_CHANGE_STATE;
+ // Find which things can change the state.
+ auto stateChangingImports =
+ runner->options.getArgumentOrDefault("bysyncify-imports", "");
std::string separator = ",";
- stateChangingImports = separator + stateChangingImports + separator;
+ auto ignoreImports =
+ runner->options.getArgumentOrDefault("bysyncify-ignore-imports", "");
+ bool allImportsCanChangeState =
+ stateChangingImports == "" && ignoreImports == "";
+ if (!allImportsCanChangeState) {
+ stateChangingImports = separator + stateChangingImports + separator;
+ }
+ auto ignoreIndirect =
+ runner->options.getArgumentOrDefault("bysyncify-ignore-indirect", "");
// Scan the module.
- ModuleAnalyzer analyzer(*module, [&](Name module, Name base) {
- if (allImportsCanChangeState) {
- return true;
- }
- std::string full = separator + module.str + '.' + base.str + separator;
- return stateChangingImports.find(full) != std::string::npos;
- });
+ ModuleAnalyzer analyzer(
+ *module,
+ [&](Name module, Name base) {
+ if (allImportsCanChangeState) {
+ return true;
+ }
+ std::string full = separator + module.str + '.' + base.str + separator;
+ return stateChangingImports.find(full) != std::string::npos;
+ },
+ ignoreIndirect == "");
// Add necessary globals before we emit code to use them.
addGlobals(module);