Skip to content

Commit 8e78dbc

Browse files
MAIN_THREAD_EM_ASM support (#2367)
* Support for sync and async main-thread EM_ASM * Fix up import names as well * update test * fix whitespace * clang-format
1 parent 3c78269 commit 8e78dbc

File tree

3 files changed

+579
-71
lines changed

3 files changed

+579
-71
lines changed

src/wasm/wasm-emscripten.cpp

Lines changed: 113 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232
namespace wasm {
3333

34-
cashew::IString EMSCRIPTEN_ASM_CONST("emscripten_asm_const");
34+
cashew::IString EM_ASM_PREFIX("emscripten_asm_const");
3535
cashew::IString EM_JS_PREFIX("__em_js__");
3636

3737
static Name STACK_SAVE("stackSave");
@@ -664,13 +664,36 @@ std::string codeForConstAddr(Module& wasm,
664664
return escape(str);
665665
}
666666

667+
enum class Proxying {
668+
None,
669+
Sync,
670+
Async,
671+
};
672+
673+
std::string proxyingSuffix(Proxying proxy) {
674+
switch (proxy) {
675+
case Proxying::None:
676+
return "";
677+
case Proxying::Sync:
678+
return "sync_on_main_thread_";
679+
case Proxying::Async:
680+
return "async_on_main_thread_";
681+
}
682+
WASM_UNREACHABLE();
683+
}
684+
667685
struct AsmConstWalker : public LinearExecutionWalker<AsmConstWalker> {
668686
Module& wasm;
669687
std::vector<Address> segmentOffsets; // segment index => address offset
670688

671-
std::map<std::string, std::set<std::string>> sigsForCode;
672-
std::map<std::string, Address> ids;
673-
std::set<std::string> allSigs;
689+
struct AsmConst {
690+
std::set<std::string> sigs;
691+
Address id;
692+
Proxying proxy;
693+
};
694+
695+
std::map<std::string, AsmConst> asmConsts;
696+
std::set<std::pair<std::string, Proxying>> allSigs;
674697
// last sets in the current basic block, per index
675698
std::map<Index, LocalSet*> sets;
676699

@@ -686,12 +709,13 @@ struct AsmConstWalker : public LinearExecutionWalker<AsmConstWalker> {
686709
void process();
687710

688711
private:
689-
std::string fixupNameWithSig(Name& name, std::string baseSig);
690-
Literal idLiteralForCode(std::string code);
712+
std::string fixupName(Name& name, std::string baseSig, Proxying proxy);
713+
AsmConst& createAsmConst(std::string code, std::string sig, Name name);
691714
std::string asmConstSig(std::string baseSig);
692-
Name nameForImportWithSig(std::string sig);
715+
Name nameForImportWithSig(std::string sig, Proxying proxy);
693716
void queueImport(Name importName, std::string baseSig);
694717
void addImports();
718+
Proxying proxyType(Name name);
695719

696720
std::vector<std::unique_ptr<Function>> queuedImports;
697721
};
@@ -707,57 +731,74 @@ void AsmConstWalker::visitCall(Call* curr) {
707731
auto* import = wasm.getFunction(curr->target);
708732
// Find calls to emscripten_asm_const* functions whose first argument is
709733
// is always a string constant.
710-
if (import->imported() && import->base.hasSubstring(EMSCRIPTEN_ASM_CONST)) {
711-
auto baseSig = getSig(curr);
712-
auto sig = fixupNameWithSig(curr->target, baseSig);
713-
auto* arg = curr->operands[0];
714-
while (!arg->dynCast<Const>()) {
715-
if (auto* get = arg->dynCast<LocalGet>()) {
716-
// The argument may be a local.get, in which case, the last set in this
717-
// basic block has the value.
718-
auto* set = sets[get->index];
719-
if (set) {
720-
assert(set->index == get->index);
721-
arg = set->value;
722-
} else {
723-
Fatal() << "local.get of unknown in arg0 of call to " << import->base
724-
<< " (used by EM_ASM* macros) in function "
725-
<< getFunction()->name
726-
<< ".\nThis might be caused by aggressive compiler "
727-
"transformations. Consider using EM_JS instead.";
728-
}
729-
} else if (auto* value = arg->dynCast<Binary>()) {
730-
// In the dynamic linking case the address of the string constant
731-
// is the result of adding its offset to __memory_base.
732-
// In this case are only looking for the offset with the data segment so
733-
// the RHS of the addition is just what we want.
734-
assert(value->op == AddInt32);
735-
arg = value->right;
734+
if (!import->imported()) {
735+
return;
736+
}
737+
auto importName = import->base;
738+
if (!importName.hasSubstring(EM_ASM_PREFIX)) {
739+
return;
740+
}
741+
742+
auto baseSig = getSig(curr);
743+
auto sig = asmConstSig(baseSig);
744+
auto* arg = curr->operands[0];
745+
while (!arg->dynCast<Const>()) {
746+
if (auto* get = arg->dynCast<LocalGet>()) {
747+
// The argument may be a local.get, in which case, the last set in this
748+
// basic block has the value.
749+
auto* set = sets[get->index];
750+
if (set) {
751+
assert(set->index == get->index);
752+
arg = set->value;
736753
} else {
737-
if (!value) {
738-
Fatal() << "Unexpected arg0 type (" << getExpressionName(arg)
739-
<< ") in call to: " << import->base;
740-
}
754+
Fatal() << "local.get of unknown in arg0 of call to " << importName
755+
<< " (used by EM_ASM* macros) in function "
756+
<< getFunction()->name
757+
<< ".\nThis might be caused by aggressive compiler "
758+
"transformations. Consider using EM_JS instead.";
759+
}
760+
} else if (auto* value = arg->dynCast<Binary>()) {
761+
// In the dynamic linking case the address of the string constant
762+
// is the result of adding its offset to __memory_base.
763+
// In this case are only looking for the offset with the data segment so
764+
// the RHS of the addition is just what we want.
765+
assert(value->op == AddInt32);
766+
arg = value->right;
767+
} else {
768+
if (!value) {
769+
Fatal() << "Unexpected arg0 type (" << getExpressionName(arg)
770+
<< ") in call to: " << importName;
741771
}
742772
}
773+
}
743774

744-
auto* value = arg->cast<Const>();
745-
auto code = codeForConstAddr(wasm, segmentOffsets, value);
746-
sigsForCode[code].insert(sig);
775+
auto* value = arg->cast<Const>();
776+
auto code = codeForConstAddr(wasm, segmentOffsets, value);
777+
auto& asmConst = createAsmConst(code, sig, importName);
778+
fixupName(curr->target, baseSig, asmConst.proxy);
747779

748-
// Replace the first argument to the call with a Const index
749-
Builder builder(wasm);
750-
curr->operands[0] = builder.makeConst(idLiteralForCode(code));
780+
// Replace the first argument to the call with a Const index
781+
Builder builder(wasm);
782+
curr->operands[0] = builder.makeConst(Literal(asmConst.id));
783+
}
784+
785+
Proxying AsmConstWalker::proxyType(Name name) {
786+
if (name.hasSubstring("_sync_on_main_thread")) {
787+
return Proxying::Sync;
788+
} else if (name.hasSubstring("_async_on_main_thread")) {
789+
return Proxying::Async;
751790
}
791+
return Proxying::None;
752792
}
753793

754794
void AsmConstWalker::visitTable(Table* curr) {
755795
for (auto& segment : curr->segments) {
756796
for (auto& name : segment.data) {
757797
auto* func = wasm.getFunction(name);
758-
if (func->imported() && func->base.hasSubstring(EMSCRIPTEN_ASM_CONST)) {
798+
if (func->imported() && func->base.hasSubstring(EM_ASM_PREFIX)) {
759799
std::string baseSig = getSig(func);
760-
fixupNameWithSig(name, baseSig);
800+
auto proxy = proxyType(func->base);
801+
fixupName(name, baseSig, proxy);
761802
}
762803
}
763804
}
@@ -771,27 +812,31 @@ void AsmConstWalker::process() {
771812
addImports();
772813
}
773814

774-
std::string AsmConstWalker::fixupNameWithSig(Name& name, std::string baseSig) {
815+
std::string
816+
AsmConstWalker::fixupName(Name& name, std::string baseSig, Proxying proxy) {
775817
auto sig = asmConstSig(baseSig);
776-
auto importName = nameForImportWithSig(sig);
818+
auto importName = nameForImportWithSig(sig, proxy);
777819
name = importName;
778820

779-
if (allSigs.count(sig) == 0) {
780-
allSigs.insert(sig);
821+
auto pair = std::make_pair(sig, proxy);
822+
if (allSigs.count(pair) == 0) {
823+
allSigs.insert(pair);
781824
queueImport(importName, baseSig);
782825
}
783826
return sig;
784827
}
785828

786-
Literal AsmConstWalker::idLiteralForCode(std::string code) {
787-
int32_t id;
788-
if (ids.count(code) == 0) {
789-
id = ids.size();
790-
ids[code] = id;
791-
} else {
792-
id = ids[code];
829+
AsmConstWalker::AsmConst&
830+
AsmConstWalker::createAsmConst(std::string code, std::string sig, Name name) {
831+
if (asmConsts.count(code) == 0) {
832+
AsmConst asmConst;
833+
asmConst.id = asmConsts.size();
834+
asmConst.sigs.insert(sig);
835+
asmConst.proxy = proxyType(name);
836+
837+
asmConsts[code] = asmConst;
793838
}
794-
return Literal(id);
839+
return asmConsts[code];
795840
}
796841

797842
std::string AsmConstWalker::asmConstSig(std::string baseSig) {
@@ -806,8 +851,9 @@ std::string AsmConstWalker::asmConstSig(std::string baseSig) {
806851
return sig;
807852
}
808853

809-
Name AsmConstWalker::nameForImportWithSig(std::string sig) {
810-
std::string fixedTarget = EMSCRIPTEN_ASM_CONST.str + std::string("_") + sig;
854+
Name AsmConstWalker::nameForImportWithSig(std::string sig, Proxying proxy) {
855+
std::string fixedTarget =
856+
EM_ASM_PREFIX.str + std::string("_") + proxyingSuffix(proxy) + sig;
811857
return Name(fixedTarget.c_str());
812858
}
813859

@@ -830,7 +876,7 @@ AsmConstWalker fixEmAsmConstsAndReturnWalker(Module& wasm) {
830876
// This would find our generated functions if we ran it later
831877
std::vector<Name> toRemove;
832878
for (auto& import : wasm.functions) {
833-
if (import->imported() && import->base.hasSubstring(EMSCRIPTEN_ASM_CONST)) {
879+
if (import->imported() && import->base.hasSubstring(EM_ASM_PREFIX)) {
834880
toRemove.push_back(import->name);
835881
}
836882
}
@@ -1039,19 +1085,15 @@ std::string EmscriptenGlueGenerator::generateEmscriptenMetadata(
10391085

10401086
// print
10411087
commaFirst = true;
1042-
if (!emAsmWalker.sigsForCode.empty()) {
1088+
if (!emAsmWalker.asmConsts.empty()) {
10431089
meta << " \"asmConsts\": {";
1044-
for (auto& pair : emAsmWalker.sigsForCode) {
1090+
for (auto& pair : emAsmWalker.asmConsts) {
10451091
auto& code = pair.first;
1046-
auto& sigs = pair.second;
1092+
auto& asmConst = pair.second;
10471093
meta << nextElement();
1048-
meta << '"' << emAsmWalker.ids[code] << "\": [\"" << code << "\", ";
1049-
printSet(meta, sigs);
1050-
meta << ", ";
1051-
1052-
// TODO: proxying to main thread. Currently this is unsupported, so proxy
1053-
// mode is "none", represented by an empty string.
1054-
meta << "[\"\"]";
1094+
meta << '"' << asmConst.id << "\": [\"" << code << "\", ";
1095+
printSet(meta, asmConst.sigs);
1096+
meta << ", [\"" << proxyingSuffix(asmConst.proxy) << "\"]";
10551097

10561098
meta << "]";
10571099
}
@@ -1097,7 +1139,7 @@ std::string EmscriptenGlueGenerator::generateEmscriptenMetadata(
10971139
commaFirst = true;
10981140
ModuleUtils::iterImportedFunctions(wasm, [&](Function* import) {
10991141
if (emJsWalker.codeByName.count(import->base.str) == 0 &&
1100-
!import->base.startsWith(EMSCRIPTEN_ASM_CONST.str) &&
1142+
!import->base.startsWith(EM_ASM_PREFIX.str) &&
11011143
!import->base.startsWith("invoke_")) {
11021144
if (declares.insert(import->base.str).second) {
11031145
meta << nextElement() << '"' << import->base.str << '"';

0 commit comments

Comments
 (0)