diff options
-rw-r--r-- | src/passes/Asyncify.cpp | 124 |
1 files changed, 53 insertions, 71 deletions
diff --git a/src/passes/Asyncify.cpp b/src/passes/Asyncify.cpp index 2f2c1cfaf..a6afd3444 100644 --- a/src/passes/Asyncify.cpp +++ b/src/passes/Asyncify.cpp @@ -298,68 +298,54 @@ enum class DataOffset { BStackPos = 0, BStackEnd = 4 }; const auto STACK_ALIGN = 4; -// A helper class for managing fake call names. Creates the targets and +// A helper class for managing fake global names. Creates the globals and // provides mappings for using them. -// Fake calls are used to stash and then use return values from calls. We -// need to store them somewhere that is valid Binaryen IR, but also will be -// ignored by the Asyncify instrumentation, so we don't want to use a local. -// What we do is replace the local used to receive a call's result with a -// fake call to stash it, then do a fake call to receive it afterwards. (We -// do it in two steps so that if we are async, we only do the first and -// not the second, i.e., we don't store to the target local if not running -// normally). -class FakeCallHelper { +// Fake globals are used to stash and then use return values from calls. We need +// to store them somewhere that is valid Binaryen IR, but also will be ignored +// by the Asyncify instrumentation, so we don't want to use a local. What we do +// is replace the local used to receive a call's result with a fake global.set +// to stash it, then do a fake global.get to receive it afterwards. (We do it in +// two steps so that if we are async, we only do the first and not the second, +// i.e., we don't store to the target local if not running normally). +class FakeGlobalHelper { Module& module; public: - FakeCallHelper(Module& module) : module(module) { + FakeGlobalHelper(Module& module) : module(module) { + map[Type::i32] = "asyncify_fake_call_global_i32"; + map[Type::i64] = "asyncify_fake_call_global_i64"; + map[Type::f32] = "asyncify_fake_call_global_f32"; + map[Type::f64] = "asyncify_fake_call_global_f64"; Builder builder(module); - std::string prefix = "__asyncify_fake_call"; - for (auto type : {Type::i32, Type::i64, Type::f32, Type::f64}) { - auto typedPrefix = prefix + '_' + Type(type).toString(); - auto setter = typedPrefix + "_set"; - setterToTypes[setter] = type; - typeToSetters[type] = setter; - module.addFunction(builder.makeFunction( - setter, Signature(type, Type::none), {}, builder.makeUnreachable())); - auto getter = typedPrefix + "_get"; - getterToTypes[getter] = type; - typeToGetters[type] = getter; - module.addFunction(builder.makeFunction( - getter, Signature(Type::none, type), {}, builder.makeNop())); + for (auto& pair : map) { + auto type = pair.first; + auto name = pair.second; + rev[name] = type; + module.addGlobal(builder.makeGlobal( + name, type, LiteralUtils::makeZero(type, module), Builder::Mutable)); } } - ~FakeCallHelper() { - for (auto& pair : typeToSetters) { + ~FakeGlobalHelper() { + for (auto& pair : map) { auto name = pair.second; - module.removeFunction(name); - } - for (auto& pair : typeToGetters) { - auto name = pair.second; - module.removeFunction(name); + module.removeGlobal(name); } } - Name getGetter(Type type) { return typeToGetters.at(type); } - Name getSetter(Type type) { return typeToSetters.at(type); } + Name getName(Type type) { return map.at(type); } - Function* getGetter(Name name) { - if (getterToTypes.count(name)) { - return module.getFunction(name); - } - return nullptr; - } - Function* getSetter(Name name) { - if (setterToTypes.count(name)) { - return module.getFunction(name); + Type getTypeOrNone(Name name) { + auto iter = rev.find(name); + if (iter != rev.end()) { + return iter->second; } - return nullptr; + return Type::none; } private: - std::map<Type, Name> typeToSetters, typeToGetters; - std::map<Name, Type> getterToTypes, setterToTypes; + std::map<Type, Name> map; + std::map<Name, Type> rev; }; class PatternMatcher { @@ -461,7 +447,7 @@ public: const String::Split& whitelistInput, bool asserts) : module(module), canIndirectChangeState(canIndirectChangeState), - fakeCalls(module), asserts(asserts) { + fakeGlobals(module), asserts(asserts) { PatternMatcher blacklist("black", module, blacklistInput); PatternMatcher whitelist("white", module, whitelistInput); @@ -667,7 +653,7 @@ public: return walker.canChangeState; } - FakeCallHelper fakeCalls; + FakeGlobalHelper fakeGlobals; bool asserts; }; @@ -913,11 +899,9 @@ private: // AsyncifyLocals locals adds local saving/restoring. auto* set = curr->dynCast<LocalSet>(); if (set) { - auto type = set->value->type; - curr = builder->makeCall( - analyzer->fakeCalls.getSetter(type), {set->value}, type); - set->value = - builder->makeCall(analyzer->fakeCalls.getGetter(type), {}, type); + auto name = analyzer->fakeGlobals.getName(set->value->type); + curr = builder->makeGlobalSet(name, set->value); + set->value = builder->makeGlobalGet(name, set->value->type); } // Instrument the call itself (or, if a drop, the drop+call). auto index = callIndex++; @@ -1031,34 +1015,17 @@ struct AsyncifyLocals : public WalkerPass<PostWalker<AsyncifyLocals>> { AsyncifyLocals(ModuleAnalyzer* analyzer) : analyzer(analyzer) {} void visitCall(Call* curr) { - // Check if this is one of our fake calls. - if (analyzer->fakeCalls.getSetter(curr->target)) { - assert(curr->operands.size() == 1); - auto type = curr->operands[0]->type; - replaceCurrent( - builder->makeLocalSet(getFakeCallLocal(type), curr->operands[0])); - return; - } - if (analyzer->fakeCalls.getGetter(curr->target)) { - auto type = curr->type; - replaceCurrent(builder->makeLocalGet(getFakeCallLocal(type), type)); - return; - } // Replace calls to the fake intrinsics. if (curr->target == ASYNCIFY_UNWIND) { replaceCurrent(builder->makeBreak(ASYNCIFY_UNWIND, curr->operands[0])); - return; - } - if (curr->target == ASYNCIFY_GET_CALL_INDEX) { + } else if (curr->target == ASYNCIFY_GET_CALL_INDEX) { replaceCurrent(builder->makeSequence( builder->makeIncStackPos(-4), builder->makeLocalSet( rewindIndex, builder->makeLoad( 4, false, 0, 4, builder->makeGetStackPos(), Type::i32)))); - return; - } - if (curr->target == ASYNCIFY_CHECK_CALL_INDEX) { + } else if (curr->target == ASYNCIFY_CHECK_CALL_INDEX) { replaceCurrent(builder->makeBinary( EqInt32, builder->makeLocalGet(rewindIndex, Type::i32), @@ -1067,6 +1034,21 @@ struct AsyncifyLocals : public WalkerPass<PostWalker<AsyncifyLocals>> { } } + void visitGlobalSet(GlobalSet* curr) { + auto type = analyzer->fakeGlobals.getTypeOrNone(curr->name); + if (type != Type::none) { + replaceCurrent( + builder->makeLocalSet(getFakeCallLocal(type), curr->value)); + } + } + + void visitGlobalGet(GlobalGet* curr) { + auto type = analyzer->fakeGlobals.getTypeOrNone(curr->name); + if (type != Type::none) { + replaceCurrent(builder->makeLocalGet(getFakeCallLocal(type), type)); + } + } + Index getFakeCallLocal(Type type) { auto iter = fakeCallLocals.find(type); if (iter != fakeCallLocals.end()) { |