diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/asm2wasm.h | 129 | ||||
-rw-r--r-- | src/passes/Print.cpp | 2 | ||||
-rw-r--r-- | src/passes/RemoveUnusedModuleElements.cpp | 57 | ||||
-rw-r--r-- | src/tools/asm2wasm.cpp | 15 | ||||
-rw-r--r-- | src/wasm-emscripten.cpp | 4 | ||||
-rw-r--r-- | src/wasm-emscripten.h | 2 |
6 files changed, 143 insertions, 66 deletions
diff --git a/src/asm2wasm.h b/src/asm2wasm.h index 5e751327b..8688edb87 100644 --- a/src/asm2wasm.h +++ b/src/asm2wasm.h @@ -742,6 +742,64 @@ void Asm2WasmBuilder::processAsm(Ref ast) { Ref body = asmFunction[3]; assert(body[0][0] == STRING && (body[0][1]->getIString() == IString("use asm") || body[0][1]->getIString() == IString("almost asm"))); + // extra functions that we add, that are not from the compiled code. we need + // to make sure to optimize them normally (OptimizingIncrementalModuleBuilder + // does that on the fly for compiled code) + std::vector<Function*> extraSupportFunctions; + + // first, add the memory elements. we do this before the main compile+optimize + // since the optimizer should see the memory + + // apply memory growth, if relevant + if (preprocessor.memoryGrowth) { + EmscriptenGlueGenerator generator(wasm); + auto* func = generator.generateMemoryGrowthFunction(); + extraSupportFunctions.push_back(func); + wasm.memory.max = Memory::kMaxSize; + } + + // import memory + auto memoryImport = make_unique<Import>(); + memoryImport->name = MEMORY; + memoryImport->module = ENV; + memoryImport->base = MEMORY; + memoryImport->kind = ExternalKind::Memory; + wasm.memory.exists = true; + wasm.memory.imported = true; + wasm.addImport(memoryImport.release()); + + // import table + auto tableImport = make_unique<Import>(); + tableImport->name = TABLE; + tableImport->module = ENV; + tableImport->base = TABLE; + tableImport->kind = ExternalKind::Table; + wasm.addImport(tableImport.release()); + wasm.table.exists = true; + wasm.table.imported = true; + + // Import memory offset, if not already there + { + auto* import = new Import; + import->name = Name("memoryBase"); + import->module = Name("env"); + import->base = Name("memoryBase"); + import->kind = ExternalKind::Global; + import->globalType = i32; + wasm.addImport(import); + } + + // Import table offset, if not already there + { + auto* import = new Import; + import->name = Name("tableBase"); + import->module = Name("env"); + import->base = Name("tableBase"); + import->kind = ExternalKind::Global; + import->globalType = i32; + wasm.addImport(import); + } + auto addImport = [&](IString name, Ref imported, WasmType type) { assert(imported[0] == DOT); Ref module = imported[1]; @@ -846,7 +904,7 @@ void Asm2WasmBuilder::processAsm(Ref ast) { } } } - auto import = new Import(); + auto import = make_unique<Import>(); import->name = name; import->module = moduleName; import->base = imported[2]->getIString(); @@ -877,7 +935,12 @@ void Asm2WasmBuilder::processAsm(Ref ast) { } else { import->kind = ExternalKind::Function; } - wasm.addImport(import); + // we may have already created an import for this manually + if ((name == "tableBase" || name == "memoryBase") && + (wasm.getImportOrNull(import->base) || wasm.getGlobalOrNull(import->base))) { + return; + } + wasm.addImport(import.release()); }; IString Int8Array, Int16Array, Int32Array, UInt8Array, UInt16Array, UInt32Array, Float32Array, Float64Array; @@ -915,6 +978,9 @@ void Asm2WasmBuilder::processAsm(Ref ast) { auto* func = pair.second; passRunner.runFunction(func); } + for (auto* func : extraSupportFunctions) { + passRunner.runFunction(func); + } }, debug, false /* do not validate globally yet */); } @@ -1352,65 +1418,6 @@ void Asm2WasmBuilder::processAsm(Ref ast) { wasm.removeImport(EMSCRIPTEN_DEBUGINFO); } - // apply memory growth, if relevant - if (preprocessor.memoryGrowth) { - EmscriptenGlueGenerator generator(wasm); - generator.generateMemoryGrowthFunction(); - wasm.memory.max = Memory::kMaxSize; - } - -#if 0 - // export memory - auto memoryExport = make_unique<Export>(); - memoryExport->name = MEMORY; - memoryExport->value = Name::fromInt(0); - memoryExport->kind = ExternalKind::Memory; - wasm.addExport(memoryExport.release()); -#else - // import memory - auto memoryImport = make_unique<Import>(); - memoryImport->name = MEMORY; - memoryImport->module = ENV; - memoryImport->base = MEMORY; - memoryImport->kind = ExternalKind::Memory; - wasm.memory.exists = true; - wasm.memory.imported = true; - wasm.addImport(memoryImport.release()); - - // import table - auto tableImport = make_unique<Import>(); - tableImport->name = TABLE; - tableImport->module = ENV; - tableImport->base = TABLE; - tableImport->kind = ExternalKind::Table; - wasm.addImport(tableImport.release()); - wasm.table.exists = true; - wasm.table.imported = true; - - // Import memory offset, if not already there - if (!wasm.getImportOrNull("memoryBase") && !wasm.getGlobalOrNull("memoryBase")) { - auto* import = new Import; - import->name = Name("memoryBase"); - import->module = Name("env"); - import->base = Name("memoryBase"); - import->kind = ExternalKind::Global; - import->globalType = i32; - wasm.addImport(import); - } - - // Import table offset, if not already there - if (!wasm.getImportOrNull("tableBase") && !wasm.getGlobalOrNull("tableBase")) { - auto* import = new Import; - import->name = Name("tableBase"); - import->module = Name("env"); - import->base = Name("tableBase"); - import->kind = ExternalKind::Global; - import->globalType = i32; - wasm.addImport(import); - } - -#endif - if (udivmoddi4.is() && getTempRet0.is()) { // generate a wasm-optimized __udivmoddi4 method, which we can do much more efficiently in wasm // we can only do this if we know getTempRet0 as well since we use it to figure out which minified global is tempRet0 diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index cfdfc09dd..7651414e0 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -735,6 +735,7 @@ struct PrintSExpression : public Visitor<PrintSExpression> { o << " anyfunc)"; } void visitTable(Table *curr) { + if (!curr->exists) return; // if table wasn't imported, declare it if (!curr->imported) { doIndent(o, indent); @@ -764,6 +765,7 @@ struct PrintSExpression : public Visitor<PrintSExpression> { o << ")"; } void visitMemory(Memory* curr) { + if (!curr->exists) return; // if memory wasn't imported, declare it if (!curr->imported) { doIndent(o, indent); diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp index 8bf2f9c9a..13caaccc9 100644 --- a/src/passes/RemoveUnusedModuleElements.cpp +++ b/src/passes/RemoveUnusedModuleElements.cpp @@ -43,6 +43,8 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> { Module* module; std::vector<ModuleElement> queue; std::set<ModuleElement> reachable; + bool usesMemory = false; + bool usesTable = false; ReachabilityAnalyzer(Module* module, const std::vector<ModuleElement>& roots) : module(module) { queue = roots; @@ -86,6 +88,9 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> { queue.emplace_back(ModuleElementKind::Function, curr->target); } } + void visitCallIndirect(CallIndirect* curr) { + usesTable = true; + } void visitGetGlobal(GetGlobal* curr) { if (reachable.count(ModuleElement(ModuleElementKind::Global, curr->name)) == 0) { @@ -97,6 +102,30 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> { queue.emplace_back(ModuleElementKind::Global, curr->name); } } + + void visitLoad(Load* curr) { + usesMemory = true; + } + void visitStore(Store* curr) { + usesMemory = true; + } + void visitAtomicCmpxchg(AtomicCmpxchg* curr) { + usesMemory = true; + } + void visitAtomicRMW(AtomicRMW* curr) { + usesMemory = true; + } + void visitAtomicWait(AtomicWait* curr) { + usesMemory = true; + } + void visitAtomicWake(AtomicWake* curr) { + usesMemory = true; + } + void visitHost(Host* curr) { + if (curr->op == CurrentMemory || curr->op == GrowMemory) { + usesMemory = true; + } + } }; // Finds function type usage @@ -136,11 +165,17 @@ struct RemoveUnusedModuleElements : public Pass { roots.emplace_back(ModuleElementKind::Function, module->start); } // Exports are roots. + bool exportsMemory = false; + bool exportsTable = false; for (auto& curr : module->exports) { if (curr->kind == ExternalKind::Function) { roots.emplace_back(ModuleElementKind::Function, curr->value); } else if (curr->kind == ExternalKind::Global) { roots.emplace_back(ModuleElementKind::Global, curr->value); + } else if (curr->kind == ExternalKind::Memory) { + exportsMemory = true; + } else if (curr->kind == ExternalKind::Table) { + exportsTable = true; } } // For now, all functions that can be called indirectly are marked as roots. @@ -176,6 +211,28 @@ struct RemoveUnusedModuleElements : public Pass { }), v.end()); } module->updateMaps(); + // Handle the memory and table + if (!exportsMemory && !analyzer.usesMemory && module->memory.segments.empty()) { + module->memory.exists = false; + module->memory.imported = false; + module->memory.initial = 0; + module->memory.max = 0; + removeImport(ExternalKind::Memory, module); + } + if (!exportsTable && !analyzer.usesTable && module->table.segments.empty()) { + module->table.exists = false; + module->table.imported = false; + module->table.initial = 0; + module->table.max = 0; + removeImport(ExternalKind::Table, module); + } + } + + void removeImport(ExternalKind kind, Module* module) { + auto& v = module->imports; + v.erase(std::remove_if(v.begin(), v.end(), [&](const std::unique_ptr<Import>& curr) { + return curr->kind == kind; + }), v.end()); } void optimizeFunctionTypes(Module* module) { diff --git a/src/tools/asm2wasm.cpp b/src/tools/asm2wasm.cpp index 3f70e0388..75a2c4d15 100644 --- a/src/tools/asm2wasm.cpp +++ b/src/tools/asm2wasm.cpp @@ -159,11 +159,12 @@ int main(int argc, const char *argv[]) { if (options.debug) std::cerr << "wasming..." << std::endl; Module wasm; + + // set up memory wasm.memory.initial = wasm.memory.max = totalMemory / Memory::kPageSize; - Asm2WasmBuilder asm2wasm(wasm, pre, options.debug, trapMode, options.passOptions, legalizeJavaScriptFFI, options.runningDefaultOptimizationPasses(), wasmOnly); - asm2wasm.processAsm(asmjs); - // import mem init file, if provided + // import mem init file, if provided (do this before compiling the module, + // since the optimizer should see the memory segments) const auto &memInit = options.extra.find("mem init"); if (memInit != options.extra.end()) { auto filename = memInit->second.c_str(); @@ -177,6 +178,14 @@ int main(int argc, const char *argv[]) { init = Builder(wasm).makeConst(Literal(int32_t(atoi(memBase->second.c_str())))); } wasm.memory.segments.emplace_back(init, data); + } + + // compile the code + Asm2WasmBuilder asm2wasm(wasm, pre, options.debug, trapMode, options.passOptions, legalizeJavaScriptFFI, options.runningDefaultOptimizationPasses(), wasmOnly); + asm2wasm.processAsm(asmjs); + + // finalize the imported mem init + if (memInit != options.extra.end()) { if (options.runningDefaultOptimizationPasses()) { PassRunner runner(&wasm); runner.setFeatures(options.features); diff --git a/src/wasm-emscripten.cpp b/src/wasm-emscripten.cpp index 6961fc75f..a07254061 100644 --- a/src/wasm-emscripten.cpp +++ b/src/wasm-emscripten.cpp @@ -122,7 +122,7 @@ void EmscriptenGlueGenerator::generateRuntimeFunctions() { generateStackRestoreFunction(); } -void EmscriptenGlueGenerator::generateMemoryGrowthFunction() { +Function* EmscriptenGlueGenerator::generateMemoryGrowthFunction() { Name name(GROW_WASM_MEMORY); std::vector<NameType> params { { NEW_SIZE, i32 } }; Function* growFunction = builder.makeFunction( @@ -135,6 +135,8 @@ void EmscriptenGlueGenerator::generateMemoryGrowthFunction() { ); addExportedFunction(wasm, growFunction); + + return growFunction; } static bool hasI64ResultOrParam(FunctionType* ft) { diff --git a/src/wasm-emscripten.h b/src/wasm-emscripten.h index 90a7d9a86..0a0f88574 100644 --- a/src/wasm-emscripten.h +++ b/src/wasm-emscripten.h @@ -32,7 +32,7 @@ public: stackPointerOffset(stackPointerOffset) { } void generateRuntimeFunctions(); - void generateMemoryGrowthFunction(); + Function* generateMemoryGrowthFunction(); // Create thunks for use with emscripten Runtime.dynCall. Creates one for each // signature in the indirect function table. |