Skip to content

Commit

Permalink
Generate dynCall function for all signature used by invoke functi…
Browse files Browse the repository at this point in the history
…ons. (#2095)

Previously we were only creating `dynCall` functions for signatures
that we have statically in the table.  However for dynamic linking we
may need to invoke functions that we don't have table entries for
(e.g. table entries from a different module).
  • Loading branch information
sbc100 authored May 10, 2019
1 parent 4b35f38 commit 70434ef
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 29 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ full changeset diff at the end of each section.
Current Trunk
-------------

v84
---

- Generate dynCall thunks for any signatures used in "invoke" calls.

v81
---

Expand Down
4 changes: 4 additions & 0 deletions src/wasm-emscripten.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,14 @@ class EmscriptenGlueGenerator {
Builder builder;
Address stackPointerOffset;
bool useStackPointerGlobal;
// Used by generateDynCallThunk to track all the dynCall functions created
// so far.
std::unordered_set<std::string> sigs;

Global* getStackPointerGlobal();
Expression* generateLoadStackPointer();
Expression* generateStoreStackPointer(Expression* value);
void generateDynCallThunk(std::string sig);
void generateStackSaveFunction();
void generateStackAllocFunction();
void generateStackRestoreFunction();
Expand Down
66 changes: 37 additions & 29 deletions src/wasm/wasm-emscripten.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,41 +334,44 @@ inline void exportFunction(Module& wasm, Name name, bool must_export) {
wasm.addExport(exp);
}

void EmscriptenGlueGenerator::generateDynCallThunk(std::string sig) {
auto* funcType = ensureFunctionType(sig, &wasm);
if (!sigs.insert(sig).second) {
return; // sig is already in the set
}
Name name = std::string("dynCall_") + sig;
if (wasm.getFunctionOrNull(name) || wasm.getExportOrNull(name)) {
return; // module already contains this dyncall
}
std::vector<NameType> params;
params.emplace_back("fptr", i32); // function pointer param
int p = 0;
for (const auto& ty : funcType->params) {
params.emplace_back(std::to_string(p++), ty);
}
Function* f =
builder.makeFunction(name, std::move(params), funcType->result, {});
Expression* fptr = builder.makeGetLocal(0, i32);
std::vector<Expression*> args;
for (unsigned i = 0; i < funcType->params.size(); ++i) {
args.push_back(builder.makeGetLocal(i + 1, funcType->params[i]));
}
Expression* call = builder.makeCallIndirect(funcType, fptr, args);
f->body = call;

wasm.addFunction(f);
exportFunction(wasm, f->name, true);
}

void EmscriptenGlueGenerator::generateDynCallThunks() {
std::unordered_set<std::string> sigs;
Builder builder(wasm);
std::vector<Name> tableSegmentData;
if (wasm.table.segments.size() > 0) {
tableSegmentData = wasm.table.segments[0].data;
}
for (const auto& indirectFunc : tableSegmentData) {
std::string sig = getSig(wasm.getFunction(indirectFunc));
auto* funcType = ensureFunctionType(sig, &wasm);
if (!sigs.insert(sig).second) {
continue; // sig is already in the set
}
Name name = std::string("dynCall_") + sig;
if (wasm.getFunctionOrNull(name) || wasm.getExportOrNull(name)) {
continue; // module already contains this dyncall
}
std::vector<NameType> params;
params.emplace_back("fptr", i32); // function pointer param
int p = 0;
for (const auto& ty : funcType->params) {
params.emplace_back(std::to_string(p++), ty);
}
Function* f =
builder.makeFunction(name, std::move(params), funcType->result, {});
Expression* fptr = builder.makeGetLocal(0, i32);
std::vector<Expression*> args;
for (unsigned i = 0; i < funcType->params.size(); ++i) {
args.push_back(builder.makeGetLocal(i + 1, funcType->params[i]));
}
Expression* call = builder.makeCallIndirect(funcType, fptr, args);
f->body = call;

wasm.addFunction(f);
exportFunction(wasm, f->name, true);
generateDynCallThunk(sig);
}
}

Expand Down Expand Up @@ -769,6 +772,7 @@ struct FixInvokeFunctionNamesWalker
std::map<Name, Name> importRenames;
std::vector<Name> toRemove;
std::set<Name> newImports;
std::set<std::string> invokeSigs;

FixInvokeFunctionNamesWalker(Module& _wasm) : wasm(_wasm) {}

Expand All @@ -791,7 +795,7 @@ struct FixInvokeFunctionNamesWalker
// This function converts the names of invoke wrappers based on their lowered
// argument types and a return type. In the example above, the resulting new
// wrapper name becomes "invoke_vii".
static Name fixEmExceptionInvoke(const Name& name, const std::string& sig) {
Name fixEmExceptionInvoke(const Name& name, const std::string& sig) {
std::string nameStr = name.c_str();
if (nameStr.front() == '"' && nameStr.back() == '"') {
nameStr = nameStr.substr(1, nameStr.size() - 2);
Expand All @@ -800,10 +804,11 @@ struct FixInvokeFunctionNamesWalker
return name;
}
std::string sigWoOrigFunc = sig.front() + sig.substr(2, sig.size() - 2);
invokeSigs.insert(sigWoOrigFunc);
return Name("invoke_" + sigWoOrigFunc);
}

static Name fixEmEHSjLjNames(const Name& name, const std::string& sig) {
Name fixEmEHSjLjNames(const Name& name, const std::string& sig) {
if (name == "emscripten_longjmp_jmpbuf") {
return "emscripten_longjmp";
}
Expand Down Expand Up @@ -843,6 +848,9 @@ struct FixInvokeFunctionNamesWalker
void EmscriptenGlueGenerator::fixInvokeFunctionNames() {
FixInvokeFunctionNamesWalker walker(wasm);
walker.walkModule(&wasm);
for (auto sig : walker.invokeSigs) {
generateDynCallThunk(sig);
}
}

template<class C> void printSet(std::ostream& o, C& c) {
Expand Down

0 comments on commit 70434ef

Please sign in to comment.