diff --git a/lld/test/wasm/memory-naming.test b/lld/test/wasm/memory-naming.test new file mode 100644 --- /dev/null +++ b/lld/test/wasm/memory-naming.test @@ -0,0 +1,88 @@ +# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/start.s -o %t.start.o +# RUN: wasm-ld --export-memory=foo -o %t.wasm %t.start.o +# RUN: obj2yaml %t.wasm | FileCheck %s + +# Verify that the --export-memory= option changes the exported name of the module's memory + +# CHECK: - Type: EXPORT +# CHECK-NEXT: Exports: +# CHECK-NEXT: - Name: foo +# CHECK-NEXT: Kind: MEMORY +# CHECK-NEXT: Index: 0 +# CHECK-NEXT: - Name: _start +# CHECK-NEXT: Kind: FUNCTION +# CHECK-NEXT: Index: 0 +# CHECK-NEXT: - Type: + +# RUN:wasm-ld --export-memory --export-memory -o %t.unnamed.wasm %t.start.o +# RUN: obj2yaml %t.unnamed.wasm | FileCheck -check-prefix=CHECK-UNNAMED %s + +# Verify that the --export-memory option without a parameter exports the memory +# as "memory" + +# CHECK-UNNAMED: - Type: EXPORT +# CHECK-UNNAMED-NEXT: Exports: +# CHECK-UNNAMED-NEXT: - Name: memory +# CHECK-UNNAMED-NEXT: Kind: MEMORY +# CHECK-UNNAMED-NEXT: Index: 0 +# CHECK-UNNAMED-NEXT: - Name: _start +# CHECK-UNNAMED-NEXT: Kind: FUNCTION +# CHECK-UNNAMED-NEXT: Index: 0 +# CHECK-UNNAMED-NEXT: - Type: + +# RUN:wasm-ld --export-memory=foo --export-memory=foo -o %t.duplicate.wasm %t.start.o +# RUN: obj2yaml %t.duplicate.wasm | FileCheck -check-prefix=CHECK-DUPLICATE %s + +# Verify that passing --export-memory with the same name twice works + +# CHECK-DUPLICATE: - Type: EXPORT +# CHECK-DUPLICATE-NEXT: Exports: +# CHECK-DUPLICATE-NEXT: - Name: foo +# CHECK-DUPLICATE-NEXT: Kind: MEMORY +# CHECK-DUPLICATE-NEXT: Index: 0 +# CHECK-DUPLICATE-NEXT: - Name: _start +# CHECK-DUPLICATE-NEXT: Kind: FUNCTION +# CHECK-DUPLICATE-NEXT: Index: 0 +# CHECK-DUPLICATE-NEXT: - Type: + +# RUN:wasm-ld --import-memory=foo,bar -o %t.import.wasm %t.start.o +# RUN: obj2yaml %t.import.wasm | FileCheck -check-prefix=CHECK-IMPORT %s + +# Verify that memory imports can be renamed, and that no memory is exported by +# default when memory is being imported + +# CHECK-IMPORT: - Type: IMPORT +# CHECK-IMPORT-NEXT: Imports: +# CHECK-IMPORT-NEXT: - Module: foo +# CHECK-IMPORT-NEXT: Field: bar +# CHECK-IMPORT-NEXT: Kind: MEMORY +# CHECK-IMPORT-NEXT: Memory: +# CHECK-IMPORT-NEXT: Minimum: 0x2 +# CHECK-IMPORT: - Type: EXPORT +# CHECK-IMPORT-NEXT: Exports: +# CHECK-IMPORT-NEXT: - Name: _start +# CHECK-IMPORT-NEXT: Kind: FUNCTION +# CHECK-IMPORT-NEXT: Index: 0 +# CHECK-IMPORT-NEXT: - Type: + +# RUN:wasm-ld --import-memory=foo,bar --export-memory=qux -o %t.both.wasm %t.start.o +# RUN: obj2yaml %t.both.wasm | FileCheck -check-prefix=CHECK-BOTH %s + +# Verify that memory can be both imported and exported from a module + +# CHECK-BOTH: - Type: IMPORT +# CHECK-BOTH-NEXT: Imports: +# CHECK-BOTH-NEXT: - Module: foo +# CHECK-BOTH-NEXT: Field: bar +# CHECK-BOTH-NEXT: Kind: MEMORY +# CHECK-BOTH-NEXT: Memory: +# CHECK-BOTH-NEXT: Minimum: 0x2 +# CHECK-BOTH: - Type: EXPORT +# CHECK-BOTH-NEXT: Exports: +# CHECK-BOTH-NEXT: - Name: qux +# CHECK-BOTH-NEXT: Kind: MEMORY +# CHECK-BOTH-NEXT: Index: 0 +# CHECK-BOTH-NEXT: - Name: _start +# CHECK-BOTH-NEXT: Kind: FUNCTION +# CHECK-BOTH-NEXT: Index: 0 +# CHECK-BOTH-NEXT: - Type: diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h --- a/lld/wasm/Config.h +++ b/lld/wasm/Config.h @@ -39,7 +39,8 @@ bool extendedConst; bool growableTable; bool gcSections; - bool importMemory; + llvm::Optional> memoryImport; + llvm::Optional memoryExport; bool sharedMemory; bool importTable; bool importUndefined; diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -393,7 +393,27 @@ config->exportAll = args.hasArg(OPT_export_all); config->exportTable = args.hasArg(OPT_export_table); config->growableTable = args.hasArg(OPT_growable_table); - config->importMemory = args.hasArg(OPT_import_memory); + + if (args.hasArg(OPT_import_memory_with_name)) { + config->memoryImport = + args.getLastArgValue(OPT_import_memory_with_name).split(","); + } else if (args.hasArg(OPT_import_memory)) { + config->memoryImport = + std::pair(defaultModule, memoryName); + } else { + config->memoryImport = + llvm::Optional>(); + } + + if (args.hasArg(OPT_export_memory_with_name)) { + config->memoryExport = + args.getLastArgValue(OPT_export_memory_with_name); + } else if (args.hasArg(OPT_export_memory)) { + config->memoryExport = memoryName; + } else { + config->memoryExport = llvm::Optional(); + } + config->sharedMemory = args.hasArg(OPT_shared_memory); config->importTable = args.hasArg(OPT_import_table); config->importUndefined = args.hasArg(OPT_import_undefined); @@ -510,9 +530,21 @@ } if (config->shared) { - config->importMemory = true; + if (config->memoryExport.hasValue()) { + error("--export-memory is incompatible with --shared"); + } + if (!config->memoryImport.hasValue()) { + config->memoryImport = + std::pair(defaultModule, memoryName); + } config->importUndefined = true; } + + // If neither export-memory nor import-memory is specified, default to + // exporting memory under its default name. + if (!config->memoryExport.hasValue() && !config->memoryImport.hasValue()) { + config->memoryExport = memoryName; + } } // Some command line options or some combinations of them are not allowed. diff --git a/lld/wasm/Options.td b/lld/wasm/Options.td --- a/lld/wasm/Options.td +++ b/lld/wasm/Options.td @@ -189,7 +189,15 @@ HelpText<"Where to start to place global data">; def import_memory: FF<"import-memory">, - HelpText<"Import memory from the environment">; + HelpText<"Import the module's memory from the default module of \"env\" with the name \"memory\".">; +def import_memory_with_name: JJ<"import-memory=">, + HelpText<"Import the module's memory from the passed module with the passed name.">, + MetaVarName<",">; + +def export_memory: FF<"export-memory">, + HelpText<"Export the module's memory with the default name of \"memory\"">; +def export_memory_with_name: JJ<"export-memory=">, + HelpText<"Export the module's memory with the passed name">; def shared_memory: FF<"shared-memory">, HelpText<"Use shared linear memory">; diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h --- a/lld/wasm/Symbols.h +++ b/lld/wasm/Symbols.h @@ -26,6 +26,9 @@ // The name under which to import or export the wasm table. extern const char *functionTableName; +// The name under which to import or export the wasm memory. +extern const char *memoryName; + using llvm::wasm::WasmSymbolType; class InputFile; diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp --- a/lld/wasm/Symbols.cpp +++ b/lld/wasm/Symbols.cpp @@ -457,6 +457,7 @@ const char *defaultModule = "env"; const char *functionTableName = "__indirect_function_table"; +const char *memoryName = "memory"; } // namespace wasm } // namespace lld diff --git a/lld/wasm/SyntheticSections.h b/lld/wasm/SyntheticSections.h --- a/lld/wasm/SyntheticSections.h +++ b/lld/wasm/SyntheticSections.h @@ -230,7 +230,7 @@ public: MemorySection() : SyntheticSection(llvm::wasm::WASM_SEC_MEMORY) {} - bool isNeeded() const override { return !config->importMemory; } + bool isNeeded() const override { return !config->memoryImport.hasValue(); } void writeBody() override; uint64_t numMemoryPages = 0; diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp --- a/lld/wasm/SyntheticSections.cpp +++ b/lld/wasm/SyntheticSections.cpp @@ -162,7 +162,7 @@ uint32_t ImportSection::getNumImports() const { assert(isSealed); uint32_t numImports = importedSymbols.size() + gotSymbols.size(); - if (config->importMemory) + if (config->memoryImport.hasValue()) ++numImports; return numImports; } @@ -234,10 +234,10 @@ bool is64 = config->is64.value_or(false); - if (config->importMemory) { + if (config->memoryImport) { WasmImport import; - import.Module = defaultModule; - import.Field = "memory"; + import.Module = config->memoryImport.getValue().first; + import.Field = config->memoryImport.getValue().second; import.Kind = WASM_EXTERNAL_MEMORY; import.Memory.Flags = 0; import.Memory.Minimum = out.memorySec->numMemoryPages; diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -577,7 +577,7 @@ // memory is not being imported then we can assume its zero initialized. // In the case the memory is imported, and we can use the memory.fill // instruction, then we can also avoid including the segments. - if (config->importMemory && !allowed.count("bulk-memory")) + if (config->memoryImport.hasValue() && !allowed.count("bulk-memory")) config->emitBssSegments = true; if (allowed.count("extended-const")) @@ -671,9 +671,10 @@ if (config->relocatable) return; - if (!config->relocatable && !config->importMemory) + if (!config->relocatable && config->memoryExport.hasValue()) { out.exportSec->exports.push_back( - WasmExport{"memory", WASM_EXTERNAL_MEMORY, 0}); + WasmExport{*config->memoryExport, WASM_EXTERNAL_MEMORY, 0}); + } unsigned globalIndex = out.importSec->getNumImportedGlobals() + out.globalSec->numGlobals(); @@ -1007,7 +1008,7 @@ bool Writer::needsPassiveInitialization(const OutputSegment *segment) { // If bulk memory features is supported then we can perform bss initialization // (via memory.fill) during `__wasm_init_memory`. - if (config->importMemory && !segment->requiredInBinary()) + if (config->memoryImport.hasValue() && !segment->requiredInBinary()) return true; return segment->initFlags & WASM_DATA_SEGMENT_IS_PASSIVE; }