diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp index b33fb5aa999..7173ec0fb64 100644 --- a/src/tools/wasm-ctor-eval.cpp +++ b/src/tools/wasm-ctor-eval.cpp @@ -115,63 +115,79 @@ class EvallingModuleRunner : public ModuleRunnerBase { } }; -// Build an artificial `env` module based on a module's imports, so that the +// Build artificial modules based on a module's imports, so that the // interpreter can use correct object instances. It initializes usable global // imports, and fills the rest with fake values since those are dangerous to -// use. we will fail if dangerous globals are used. -std::unique_ptr buildEnvModule(Module& wasm) { - auto env = std::make_unique(); - env->name = "env"; - - // create empty functions with similar signature - ModuleUtils::iterImportedFunctions(wasm, [&](Function* func) { - if (func->module == env->name) { - Builder builder(*env); - auto* copied = ModuleUtils::copyFunction(func, *env); - copied->module = Name(); - copied->base = Name(); - copied->body = builder.makeUnreachable(); - env->addExport( - builder.makeExport(func->base, copied->name, ExternalKind::Function)); - } - }); - - // create tables with similar initial and max values - ModuleUtils::iterImportedTables(wasm, [&](Table* table) { - if (table->module == env->name) { - auto* copied = ModuleUtils::copyTable(table, *env); - copied->module = Name(); - copied->base = Name(); - env->addExport(Builder(*env).makeExport( - table->base, copied->name, ExternalKind::Table)); - } - }); - - ModuleUtils::iterImportedGlobals(wasm, [&](Global* global) { - if (global->module == env->name) { - auto* copied = ModuleUtils::copyGlobal(global, *env); - copied->module = Name(); - copied->base = Name(); - - Builder builder(*env); - copied->init = builder.makeConst(Literal::makeZero(global->type)); - env->addExport( - builder.makeExport(global->base, copied->name, ExternalKind::Global)); - } - }); - - // create an exported memory with the same initial and max size - ModuleUtils::iterImportedMemories(wasm, [&](Memory* memory) { - if (memory->module == env->name) { - auto* copied = ModuleUtils::copyMemory(memory, *env); - copied->module = Name(); - copied->base = Name(); - env->addExport(Builder(*env).makeExport( - memory->base, copied->name, ExternalKind::Memory)); - } - }); +// use. Imported globals can't be read anyway; see +// `EvallingModuleRunner::visitGlobalGet`. +// Note: wasi_ modules have stubs generated but won't be called due to the +// special handling in `CtorEvalExternalInterface::getImportedFunction`. We +// still generate the stubs to ensure the link-time validation passes. +std::vector> buildStubModules(Module& wasm) { + std::map> modules; + + ModuleUtils::iterImports( + wasm, + [&modules](std::variant import) { + Importable* importable = + std::visit([](auto* i) -> Importable* { return i; }, import); + + auto [it, inserted] = modules.try_emplace(importable->module, nullptr); + if (inserted) { + it->second = std::make_unique(); + it->second->name = importable->module; + } + Module* module = it->second.get(); + + struct Visitor { + Module* module; + void operator()(Memory* memory) { + auto* copied = ModuleUtils::copyMemory(memory, *module); + copied->module = Name(); + copied->base = Name(); + module->addExport(Builder(*module).makeExport( + memory->base, copied->name, ExternalKind::Memory)); + } + void operator()(Table* table) { + // create tables with similar initial and max values + auto* copied = ModuleUtils::copyTable(table, *module); + copied->module = Name(); + copied->base = Name(); + module->addExport(Builder(*module).makeExport( + table->base, copied->name, ExternalKind::Table)); + } + void operator()(Global* global) { + auto* copied = ModuleUtils::copyGlobal(global, *module); + copied->module = Name(); + copied->base = Name(); + + Builder builder(*module); + copied->init = builder.makeConst(Literal::makeZero(global->type)); + module->addExport(builder.makeExport( + global->base, copied->name, ExternalKind::Global)); + } + void operator()(Function* func) { + Builder builder(*module); + auto* copied = ModuleUtils::copyFunction(func, *module); + copied->module = Name(); + copied->base = Name(); + copied->body = builder.makeUnreachable(); + module->addExport(builder.makeExport( + func->base, copied->name, ExternalKind::Function)); + } + void operator()(Tag* tag) { + // no-op + } + }; + std::visit(Visitor{module}, import); + }); - return env; + std::vector> modulesVector; + modulesVector.reserve(modules.size()); + for (auto& [_, ptr] : modules) { + modulesVector.push_back(std::move(ptr)); + } + return modulesVector; } // Whether to ignore external input to the program as it runs. If set, we will @@ -1356,12 +1372,16 @@ void evalCtors(Module& wasm, std::map> linkedInstances; - // build and link the env module - auto envModule = buildEnvModule(wasm); - CtorEvalExternalInterface envInterface; - auto envInstance = - std::make_shared(*envModule, &envInterface); - linkedInstances[envModule->name] = envInstance; + // stubModules and interfaces must be kept alive since they are referenced in + // linkedInstances. + std::vector> stubModules = buildStubModules(wasm); + std::vector> interfaces; + + for (auto& module : stubModules) { + interfaces.push_back(std::make_unique()); + linkedInstances[module->name] = + std::make_shared(*module, interfaces.back().get()); + } CtorEvalExternalInterface interface(linkedInstances); try { diff --git a/test/ctor-eval/global-get-init.wast b/test/ctor-eval/global-get-init.wast index 125e672d6c5..5c085e245cb 100644 --- a/test/ctor-eval/global-get-init.wast +++ b/test/ctor-eval/global-get-init.wast @@ -1,8 +1,9 @@ (module (import "import" "global" (global $imported i32)) - (func $test1 (export "test1") - ;; This should be safe to eval in theory, but the imported global stops us, - ;; so this function will not be optimized out. - ;; TODO: perhaps if we never use that global that is ok? + (func $use-global (export "use-global") (result i32) + (global.get $imported) ) + ;; The imported global isn't used in the ctor, + ;; so we're free to remove it completely. + (func $test1 (export "test1")) ) diff --git a/test/ctor-eval/global-get-init.wast.out b/test/ctor-eval/global-get-init.wast.out index 519e96dbabd..0e5138fb621 100644 --- a/test/ctor-eval/global-get-init.wast.out +++ b/test/ctor-eval/global-get-init.wast.out @@ -1,7 +1,8 @@ (module - (type $0 (func)) - (export "test1" (func $test1)) - (func $test1 (type $0) - (nop) + (type $0 (func (result i32))) + (import "import" "global" (global $imported i32)) + (export "use-global" (func $use-global)) + (func $use-global (type $0) (result i32) + (global.get $imported) ) )