167167// we are recreating the call stack. At that point you should call
168168// bysyncify_stop_rewind and then execution can resume normally.
169169//
170- // By default this pass assumes that any import may call any of the
171- // exported bysyncify methods, that is, any import may start an unwind/rewind.
172- // To customize this, you can provide an argument to wasm-opt (or another
173- // tool that can run this pass),
170+ // By default this pass is very carefuly: it assumes that any import and
171+ // any indirect call may start an unwind/rewind operation. If you know more
172+ // specific information you can inform bysyncify about that, which can reduce
173+ // a great deal of overhead, as it can instrument less code. The relevant
174+ // options to wasm-opt etc. are:
174175//
175176// --pass-arg=bysyncify-imports@module1.base1,module2.base2,module3.base3
176177//
177- // Each module.base in that comma-separated list will be considered to
178- // be an import that can unwind/rewind, and all others are assumed not to
179- // (aside from the bysyncify.* imports, which are always assumed to). To
180- // say that no import (aside from bysyncify.*) can do so (that is, the
181- // opposite of the default behavior), you can simply provide an import
182- // that doesn't exist, for example:
183-
184- // --pass-arg=bysyncify-imports@no.imports
178+ // Each module.base in that comma-separated list will be considered to
179+ // be an import that can unwind/rewind, and all others are assumed not to
180+ // (aside from the bysyncify.* imports, which are always assumed to).
181+ //
182+ // --pass-arg=bysyncify-ignore-imports
183+ //
184+ // Ignore all imports (except for bynsyncify.*), that is, assume none of
185+ // them can start an unwind/rewind. (This is effectively the same as
186+ // providing bysyncify-imports with a list of non-existent imports.)
187+ //
188+ // --pass-arg=bysyncify-ignore-indirect
189+ //
190+ // Ignore all indirect calls. This implies that you know an call stack
191+ // will never need to be unwound with an indirect call somewhere in it.
192+ // If that is true for your codebase, then this can be extremely useful
193+ // as otherwise it looks like any indirect call can go to a lot of places.
185194//
186195
187196#include " ir/effects.h"
@@ -225,6 +234,7 @@ const auto STACK_ALIGN = 4;
225234// by it.
226235class ModuleAnalyzer {
227236 Module& module ;
237+ bool canIndirectChangeState;
228238
229239 struct Info {
230240 bool canChangeState = false ;
@@ -237,8 +247,9 @@ class ModuleAnalyzer {
237247
238248public:
239249 ModuleAnalyzer (Module& module ,
240- std::function<bool (Name, Name)> canImportChangeState)
241- : module (module ) {
250+ std::function<bool (Name, Name)> canImportChangeState,
251+ bool canIndirectChangeState)
252+ : module (module ), canIndirectChangeState(canIndirectChangeState) {
242253 // Scan to see which functions can directly change the state.
243254 // Also handle the bysyncify imports, removing them (as we will implement
244255 // them later), and replace calls to them with calls to the later proper
@@ -281,15 +292,19 @@ class ModuleAnalyzer {
281292 info->callsTo .insert (target);
282293 }
283294 void visitCallIndirect (CallIndirect* curr) {
284- // TODO optimize
285- info->canChangeState = true ;
295+ if (canIndirectChangeState) {
296+ info->canChangeState = true ;
297+ }
298+ // TODO optimize the other case, at least by type
286299 }
287300 Info* info;
288301 Module* module ;
302+ bool canIndirectChangeState;
289303 };
290304 Walker walker;
291305 walker.info = &info;
292306 walker.module = &module ;
307+ walker.canIndirectChangeState = canIndirectChangeState;
293308 walker.walk (func->body );
294309 });
295310 map.swap (scanner.map );
@@ -357,16 +372,20 @@ class ModuleAnalyzer {
357372 }
358373 }
359374 void visitCallIndirect (CallIndirect* curr) {
360- // TODO optimize
361- canChangeState = true ;
375+ if (canIndirectChangeState) {
376+ canChangeState = true ;
377+ }
378+ // TODO optimize the other case, at least by type
362379 }
363380 Module* module ;
364381 Map* map;
382+ bool canIndirectChangeState;
365383 bool canChangeState = false ;
366384 };
367385 Walker walker;
368386 walker.module = &module ;
369387 walker.map = ↦
388+ walker.canIndirectChangeState = canIndirectChangeState;
370389 walker.walk (curr);
371390 return walker.canChangeState ;
372391 }
@@ -788,23 +807,31 @@ struct BysyncifyLocals : public WalkerPass<PostWalker<BysyncifyLocals>> {
788807struct Bysyncify : public Pass {
789808 void run (PassRunner* runner, Module* module ) override {
790809 bool optimize = runner->options .optimizeLevel > 0 ;
791- // Find which imports can change the state.
792- const char * ALL_IMPORTS_CAN_CHANGE_STATE = " __bysyncify_all_imports" ;
793- auto stateChangingImports = runner->options .getArgumentOrDefault (
794- " bysyncify-imports" , ALL_IMPORTS_CAN_CHANGE_STATE);
795- bool allImportsCanChangeState =
796- stateChangingImports == ALL_IMPORTS_CAN_CHANGE_STATE;
810+ // Find which things can change the state.
811+ auto stateChangingImports =
812+ runner->options .getArgumentOrDefault (" bysyncify-imports" , " " );
797813 std::string separator = " ," ;
798- stateChangingImports = separator + stateChangingImports + separator;
814+ auto ignoreImports =
815+ runner->options .getArgumentOrDefault (" bysyncify-ignore-imports" , " " );
816+ bool allImportsCanChangeState =
817+ stateChangingImports == " " && ignoreImports == " " ;
818+ if (!allImportsCanChangeState) {
819+ stateChangingImports = separator + stateChangingImports + separator;
820+ }
821+ auto ignoreIndirect =
822+ runner->options .getArgumentOrDefault (" bysyncify-ignore-indirect" , " " );
799823
800824 // Scan the module.
801- ModuleAnalyzer analyzer (*module , [&](Name module , Name base) {
802- if (allImportsCanChangeState) {
803- return true ;
804- }
805- std::string full = separator + module .str + ' .' + base.str + separator;
806- return stateChangingImports.find (full) != std::string::npos;
807- });
825+ ModuleAnalyzer analyzer (
826+ *module ,
827+ [&](Name module , Name base) {
828+ if (allImportsCanChangeState) {
829+ return true ;
830+ }
831+ std::string full = separator + module .str + ' .' + base.str + separator;
832+ return stateChangingImports.find (full) != std::string::npos;
833+ },
834+ ignoreIndirect == " " );
808835
809836 // Add necessary globals before we emit code to use them.
810837 addGlobals (module );
0 commit comments