Skip to content

wasm-emscripten-finalize: Add flags to limit dynCall creation #3070

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/passes/GenerateDynCalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,24 @@
namespace wasm {

struct GenerateDynCalls : public WalkerPass<PostWalker<GenerateDynCalls>> {
GenerateDynCalls(bool onlyI64) : onlyI64(onlyI64) {}

void visitTable(Table* table) {
if (table->segments.size() > 0) {
EmscriptenGlueGenerator generator(*getModule());
generator.onlyI64DynCalls = onlyI64;
std::vector<Name> tableSegmentData;
for (const auto& indirectFunc : table->segments[0].data) {
generator.generateDynCallThunk(
getModule()->getFunction(indirectFunc)->sig);
}
}
}

bool onlyI64;
};

Pass* createGenerateDynCallsPass() { return new GenerateDynCalls; }
Pass* createGenerateDynCallsPass() { return new GenerateDynCalls(false); }
Pass* createGenerateI64DynCallsPass() { return new GenerateDynCalls(true); }

} // namespace wasm
5 changes: 5 additions & 0 deletions src/passes/pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ void PassRegistry::registerPasses() {
registerPass("generate-dyncalls",
"generate dynCall fuctions used by emscripten ABI",
createGenerateDynCallsPass);
registerPass("generate-i64-dyncalls",
"generate dynCall functions used by emscripten ABI, but only for "
"functions with i64 in their signature (which cannot be invoked "
"via the wasm table without JavaScript BigInt support).",
createGenerateI64DynCallsPass);
registerPass(
"generate-stack-ir", "generate Stack IR", createGenerateStackIRPass);
registerPass(
Expand Down
1 change: 1 addition & 0 deletions src/passes/passes.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Pass* createFuncCastEmulationPass();
Pass* createFullPrinterPass();
Pass* createFunctionMetricsPass();
Pass* createGenerateDynCallsPass();
Pass* createGenerateI64DynCallsPass();
Pass* createGenerateStackIRPass();
Pass* createI64ToI32LoweringPass();
Pass* createInlineMainPass();
Expand Down
30 changes: 25 additions & 5 deletions src/tools/wasm-emscripten-finalize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ int main(int argc, const char* argv[]) {
bool standaloneWasm = false;
// TODO: remove after https://github.com/WebAssembly/binaryen/issues/3043
bool minimizeWasmChanges = false;
bool noDynCalls = false;
bool onlyI64DynCalls = false;

ToolOptions options("wasm-emscripten-finalize",
"Performs Emscripten-specific transforms on .wasm files");
Expand Down Expand Up @@ -174,6 +176,18 @@ int main(int argc, const char* argv[]) {
[&minimizeWasmChanges](Options* o, const std::string&) {
minimizeWasmChanges = true;
})
.add("--no-dyncalls",
"",
"",
Options::Arguments::Zero,
[&noDynCalls](Options* o, const std::string&) { noDynCalls = true; })
.add("--dyncalls-i64",
"",
"",
Options::Arguments::Zero,
[&onlyI64DynCalls](Options* o, const std::string&) {
onlyI64DynCalls = true;
})
.add_positional("INFILE",
Options::Arguments::One,
[&infile](Options* o, const std::string& argument) {
Expand Down Expand Up @@ -231,9 +245,11 @@ int main(int argc, const char* argv[]) {
}

EmscriptenGlueGenerator generator(wasm);
generator.setStandalone(standaloneWasm);
generator.setSideModule(sideModule);
generator.setMinimizeWasmChanges(minimizeWasmChanges);
generator.standalone = standaloneWasm;
generator.sideModule = sideModule;
generator.minimizeWasmChanges = minimizeWasmChanges;
generator.onlyI64DynCalls = onlyI64DynCalls;
generator.noDynCalls = noDynCalls;

generator.fixInvokeFunctionNames();

Expand Down Expand Up @@ -278,9 +294,13 @@ int main(int argc, const char* argv[]) {
passRunner.add("emscripten-pic-main-module");
}

if (!standaloneWasm) {
if (!noDynCalls && !standaloneWasm) {
// If not standalone wasm then JS is relevant and we need dynCalls.
passRunner.add("generate-dyncalls");
if (onlyI64DynCalls) {
passRunner.add("generate-i64-dyncalls");
} else {
passRunner.add("generate-dyncalls");
}
}

// Legalize the wasm, if BigInts don't make that moot.
Expand Down
15 changes: 6 additions & 9 deletions src/wasm-emscripten.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,6 @@ class EmscriptenGlueGenerator {
: wasm(wasm), builder(wasm), stackPointerOffset(stackPointerOffset),
useStackPointerGlobal(stackPointerOffset == 0) {}

void setStandalone(bool standalone_) { standalone = standalone_; }
void setSideModule(bool sideModule_) { sideModule = sideModule_; }
void setMinimizeWasmChanges(bool minimizeWasmChanges_) {
minimizeWasmChanges = minimizeWasmChanges_;
}

Function* generateMemoryGrowthFunction();
Function* generateAssignGOTEntriesFunction();
void generatePostInstantiateFunction();
Expand Down Expand Up @@ -67,14 +61,17 @@ class EmscriptenGlueGenerator {

void generateDynCallThunk(Signature sig);

bool standalone = false;
bool sideModule = false;
bool minimizeWasmChanges = false;
bool noDynCalls = false;
bool onlyI64DynCalls = false;

private:
Module& wasm;
Builder builder;
Address stackPointerOffset;
bool useStackPointerGlobal;
bool standalone;
bool sideModule;
bool minimizeWasmChanges;
// Used by generateDynCallThunk to track all the dynCall functions created
// so far.
std::unordered_set<Signature> sigs;
Expand Down
20 changes: 20 additions & 0 deletions src/wasm/wasm-emscripten.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,27 @@ inline void exportFunction(Module& wasm, Name name, bool must_export) {
wasm.addExport(exp);
}

static bool hasI64(Signature sig) {
// We only generate dynCall functions for signatures that contain
// i64. This is because any other function can be called directly
// from JavaScript using the wasm table.
for (auto t : sig.results) {
if (t.getID() == Type::i64) {
return true;
}
}
for (auto t : sig.params) {
if (t.getID() == Type::i64) {
return true;
}
}
return false;
}

void EmscriptenGlueGenerator::generateDynCallThunk(Signature sig) {
if (noDynCalls || (onlyI64DynCalls && !hasI64(sig))) {
return;
}
if (!sigs.insert(sig).second) {
return; // sig is already in the set
}
Expand Down
20 changes: 20 additions & 0 deletions test/passes/generate-i64-dyncalls.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
(module
(type $i32_=>_i64 (func (param i32) (result i64)))
(type $none_=>_i32 (func (result i32)))
(type $i32_i32_=>_i64 (func (param i32 i32) (result i64)))
(table $0 2 2 funcref)
(elem (i32.const 0) $f1 $f2)
(export "dynCall_ji" (func $dynCall_ji))
(func $f1 (result i32)
(i32.const 1024)
)
(func $f2 (param $0 i32) (result i64)
(i64.const 42)
)
(func $dynCall_ji (param $fptr i32) (param $0 i32) (result i64)
(call_indirect (type $i32_=>_i64)
(local.get $0)
(local.get $fptr)
)
)
)
10 changes: 10 additions & 0 deletions test/passes/generate-i64-dyncalls.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
(module
(func $f1 (result i32)
(i32.const 1024)
)
(func $f2 (param i32) (result i64)
(i64.const 42)
)
(table 2 2 funcref)
(elem (i32.const 0) $f1 $f2)
)