diff options
-rw-r--r-- | src/passes/Asyncify.cpp | 117 |
1 files changed, 71 insertions, 46 deletions
diff --git a/src/passes/Asyncify.cpp b/src/passes/Asyncify.cpp index 05c1c8954..2f2c1cfaf 100644 --- a/src/passes/Asyncify.cpp +++ b/src/passes/Asyncify.cpp @@ -298,47 +298,68 @@ enum class DataOffset { BStackPos = 0, BStackEnd = 4 }; const auto STACK_ALIGN = 4; -// A helper class for managing fake global names. Creates the globals and +// A helper class for managing fake call names. Creates the targets and // provides mappings for using them. -class GlobalHelper { +// 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 { Module& module; public: - GlobalHelper(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"; + FakeCallHelper(Module& module) : module(module) { Builder builder(module); - 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)); + 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())); } } - ~GlobalHelper() { - for (auto& pair : map) { + ~FakeCallHelper() { + for (auto& pair : typeToSetters) { auto name = pair.second; - module.removeGlobal(name); + module.removeFunction(name); + } + for (auto& pair : typeToGetters) { + auto name = pair.second; + module.removeFunction(name); } } - Name getName(Type type) { return map.at(type); } + Name getGetter(Type type) { return typeToGetters.at(type); } + Name getSetter(Type type) { return typeToSetters.at(type); } - Type getTypeOrNone(Name name) { - auto iter = rev.find(name); - if (iter != rev.end()) { - return iter->second; + Function* getGetter(Name name) { + if (getterToTypes.count(name)) { + return module.getFunction(name); } - return Type::none; + return nullptr; + } + Function* getSetter(Name name) { + if (setterToTypes.count(name)) { + return module.getFunction(name); + } + return nullptr; } private: - std::map<Type, Name> map; - std::map<Name, Type> rev; + std::map<Type, Name> typeToSetters, typeToGetters; + std::map<Name, Type> getterToTypes, setterToTypes; }; class PatternMatcher { @@ -440,7 +461,7 @@ public: const String::Split& whitelistInput, bool asserts) : module(module), canIndirectChangeState(canIndirectChangeState), - globals(module), asserts(asserts) { + fakeCalls(module), asserts(asserts) { PatternMatcher blacklist("black", module, blacklistInput); PatternMatcher whitelist("white", module, whitelistInput); @@ -646,7 +667,7 @@ public: return walker.canChangeState; } - GlobalHelper globals; + FakeCallHelper fakeCalls; bool asserts; }; @@ -892,9 +913,11 @@ private: // AsyncifyLocals locals adds local saving/restoring. auto* set = curr->dynCast<LocalSet>(); if (set) { - auto name = analyzer->globals.getName(set->value->type); - curr = builder->makeGlobalSet(name, set->value); - set->value = builder->makeGlobalGet(name, set->value->type); + auto type = set->value->type; + curr = builder->makeCall( + analyzer->fakeCalls.getSetter(type), {set->value}, type); + set->value = + builder->makeCall(analyzer->fakeCalls.getGetter(type), {}, type); } // Instrument the call itself (or, if a drop, the drop+call). auto index = callIndex++; @@ -1008,17 +1031,34 @@ 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])); - } else if (curr->target == ASYNCIFY_GET_CALL_INDEX) { + return; + } + 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)))); - } else if (curr->target == ASYNCIFY_CHECK_CALL_INDEX) { + return; + } + if (curr->target == ASYNCIFY_CHECK_CALL_INDEX) { replaceCurrent(builder->makeBinary( EqInt32, builder->makeLocalGet(rewindIndex, Type::i32), @@ -1027,21 +1067,6 @@ struct AsyncifyLocals : public WalkerPass<PostWalker<AsyncifyLocals>> { } } - void visitGlobalSet(GlobalSet* curr) { - auto type = analyzer->globals.getTypeOrNone(curr->name); - if (type != Type::none) { - replaceCurrent( - builder->makeLocalSet(getFakeCallLocal(type), curr->value)); - } - } - - void visitGlobalGet(GlobalGet* curr) { - auto type = analyzer->globals.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()) { |