diff --git a/src/passes/GenerateDynCalls.cpp b/src/passes/GenerateDynCalls.cpp index b46fdd843ec..5a602bd790b 100644 --- a/src/passes/GenerateDynCalls.cpp +++ b/src/passes/GenerateDynCalls.cpp @@ -34,10 +34,12 @@ namespace wasm { struct GenerateDynCalls : public WalkerPass> { + GenerateDynCalls(bool onlyI64) : onlyI64(onlyI64) {} void visitTable(Table* table) { if (table->segments.size() > 0) { EmscriptenGlueGenerator generator(*getModule()); + generator.onlyI64DynCalls = onlyI64; std::vector tableSegmentData; for (const auto& indirectFunc : table->segments[0].data) { generator.generateDynCallThunk( @@ -45,8 +47,11 @@ struct GenerateDynCalls : public WalkerPass> { } } } + + bool onlyI64; }; -Pass* createGenerateDynCallsPass() { return new GenerateDynCalls; } +Pass* createGenerateDynCallsPass() { return new GenerateDynCalls(false); } +Pass* createGenerateI64DynCallsPass() { return new GenerateDynCalls(true); } } // namespace wasm diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp index 73e5094e85c..62c4c645299 100644 --- a/src/passes/pass.cpp +++ b/src/passes/pass.cpp @@ -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( diff --git a/src/passes/passes.h b/src/passes/passes.h index 5eb6cef3c23..bee30eebc13 100644 --- a/src/passes/passes.h +++ b/src/passes/passes.h @@ -49,6 +49,7 @@ Pass* createFuncCastEmulationPass(); Pass* createFullPrinterPass(); Pass* createFunctionMetricsPass(); Pass* createGenerateDynCallsPass(); +Pass* createGenerateI64DynCallsPass(); Pass* createGenerateStackIRPass(); Pass* createI64ToI32LoweringPass(); Pass* createInlineMainPass(); diff --git a/src/tools/wasm-emscripten-finalize.cpp b/src/tools/wasm-emscripten-finalize.cpp index 1d1ea2247eb..49f8beabfef 100644 --- a/src/tools/wasm-emscripten-finalize.cpp +++ b/src/tools/wasm-emscripten-finalize.cpp @@ -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"); @@ -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) { @@ -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(); @@ -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. diff --git a/src/wasm-emscripten.h b/src/wasm-emscripten.h index fe8b8ed36ab..0303af053d1 100644 --- a/src/wasm-emscripten.h +++ b/src/wasm-emscripten.h @@ -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(); @@ -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 sigs; diff --git a/src/wasm/wasm-emscripten.cpp b/src/wasm/wasm-emscripten.cpp index fd0560fd9ed..c287087c930 100644 --- a/src/wasm/wasm-emscripten.cpp +++ b/src/wasm/wasm-emscripten.cpp @@ -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 } diff --git a/test/passes/generate-i64-dyncalls.txt b/test/passes/generate-i64-dyncalls.txt new file mode 100644 index 00000000000..c9a282d8455 --- /dev/null +++ b/test/passes/generate-i64-dyncalls.txt @@ -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) + ) + ) +) diff --git a/test/passes/generate-i64-dyncalls.wast b/test/passes/generate-i64-dyncalls.wast new file mode 100644 index 00000000000..6fbfcc3310c --- /dev/null +++ b/test/passes/generate-i64-dyncalls.wast @@ -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) +)