Skip to content

Commit cb4f94d

Browse files
[lld][WebAssembly] Add --no-growable-memory (#82890)
We recently added `--initial-heap` - an option that allows one to up the initial memory size without the burden of having to know exactly how much is needed. However, in the process of implementing support for this in Emscripten (emscripten-core/emscripten#21071), we have realized that `--initial-heap` cannot support the use-case of non-growable memories by itself, since with it we don't know what to set `--max-memory` to. We have thus agreed to move the above work forward by introducing another option to the linker (see emscripten-core/emscripten#21071 (comment)), one that would allow users to explicitly specify they want a non-growable memory. This change does this by introducing `--no-growable-memory`: an option that is mutally exclusive with `--max-memory` (for simplicity - we can also decide that it should override or be overridable by `--max-memory`. In Emscripten a similar mix of options results in `--no-growable-memory` taking precedence). The option specifies that the maximum memory size should be set to the initial memory size, effectively disallowing memory growth. Closes #81932.
1 parent 8dfc023 commit cb4f94d

File tree

6 files changed

+46
-12
lines changed

6 files changed

+46
-12
lines changed

lld/docs/WebAssembly.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ WebAssembly-specific options:
135135

136136
Maximum size of the linear memory. Default: unlimited.
137137

138+
.. option:: --no-growable-memory
139+
140+
Set maximum size of the linear memory to its initial size, disallowing memory growth.
141+
138142
By default the function table is neither imported nor exported, but defined
139143
for internal use only.
140144

lld/test/wasm/data-layout.s

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,22 @@ local_struct_internal_ptr:
103103
# CHECK-MAX-NEXT: Minimum: 0x2
104104
# CHECK-MAX-NEXT: Maximum: 0x2
105105

106+
# RUN: wasm-ld --no-entry --initial-memory=327680 --no-growable-memory \
107+
# RUN: -o %t_max.wasm %t.hello32.o
108+
# RUN: obj2yaml %t_max.wasm | FileCheck %s -check-prefix=CHECK-NO-GROWTH
109+
110+
# CHECK-NO-GROWTH: - Type: MEMORY
111+
# CHECK-NO-GROWTH-NEXT: Memories:
112+
# CHECK-NO-GROWTH-NEXT: - Flags: [ HAS_MAX ]
113+
# CHECK-NO-GROWTH-NEXT: Minimum: 0x5
114+
# CHECK-NO-GROWTH-NEXT: Maximum: 0x5
115+
116+
# RUN: not wasm-ld --max-memory=262144 --no-growable-memory \
117+
# RUN: --no-entry -o %t_max.wasm %t.hello32.o 2>&1 \
118+
# RUN: | FileCheck %s --check-prefix CHECK-NO-GROWTH-COMPAT-ERROR
119+
120+
# CHECK-NO-GROWTH-COMPAT-ERROR: --max-memory is incompatible with --no-growable-memory
121+
106122
# RUN: wasm-ld -no-gc-sections --allow-undefined --no-entry --shared-memory \
107123
# RUN: --features=atomics,bulk-memory --initial-memory=131072 \
108124
# RUN: --max-memory=131072 -o %t_max.wasm %t32.o %t.hello32.o

lld/wasm/Config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ struct Configuration {
7878
uint64_t initialHeap;
7979
uint64_t initialMemory;
8080
uint64_t maxMemory;
81+
bool noGrowableMemory;
8182
// The table offset at which to place function addresses. We reserve zero
8283
// for the null function pointer. This gets set to 1 for executables and 0
8384
// for shared libraries (since they always added to a dynamic offset at

lld/wasm/Driver.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,9 +542,15 @@ static void readConfigs(opt::InputArgList &args) {
542542
config->initialHeap = args::getInteger(args, OPT_initial_heap, 0);
543543
config->initialMemory = args::getInteger(args, OPT_initial_memory, 0);
544544
config->maxMemory = args::getInteger(args, OPT_max_memory, 0);
545+
config->noGrowableMemory = args.hasArg(OPT_no_growable_memory);
545546
config->zStackSize =
546547
args::getZOptionValue(args, OPT_z, "stack-size", WasmPageSize);
547548

549+
if (config->maxMemory != 0 && config->noGrowableMemory) {
550+
// Erroring out here is simpler than defining precedence rules.
551+
error("--max-memory is incompatible with --no-growable-memory");
552+
}
553+
548554
// Default value of exportDynamic depends on `-shared`
549555
config->exportDynamic =
550556
args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, config->shared);

lld/wasm/Options.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,9 @@ def initial_memory: JJ<"initial-memory=">,
230230
def max_memory: JJ<"max-memory=">,
231231
HelpText<"Maximum size of the linear memory">;
232232

233+
def no_growable_memory: FF<"no-growable-memory">,
234+
HelpText<"Set maximum size of the linear memory to its initial size">;
235+
233236
def no_entry: FF<"no-entry">,
234237
HelpText<"Do not output any entry point">;
235238

lld/wasm/Writer.cpp

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,7 @@ void Writer::layoutMemory() {
473473
WasmSym::heapEnd->setVA(memoryPtr);
474474
}
475475

476+
uint64_t maxMemory = 0;
476477
if (config->maxMemory != 0) {
477478
if (config->maxMemory != alignTo(config->maxMemory, WasmPageSize))
478479
error("maximum memory must be " + Twine(WasmPageSize) + "-byte aligned");
@@ -481,20 +482,23 @@ void Writer::layoutMemory() {
481482
if (config->maxMemory > maxMemorySetting)
482483
error("maximum memory too large, cannot be greater than " +
483484
Twine(maxMemorySetting));
485+
486+
maxMemory = config->maxMemory;
487+
} else if (config->noGrowableMemory) {
488+
maxMemory = memoryPtr;
484489
}
485490

486-
// Check max if explicitly supplied or required by shared memory
487-
if (config->maxMemory != 0 || config->sharedMemory) {
488-
uint64_t max = config->maxMemory;
489-
if (max == 0) {
490-
// If no maxMemory config was supplied but we are building with
491-
// shared memory, we need to pick a sensible upper limit.
492-
if (ctx.isPic)
493-
max = maxMemorySetting;
494-
else
495-
max = memoryPtr;
496-
}
497-
out.memorySec->maxMemoryPages = max / WasmPageSize;
491+
// If no maxMemory config was supplied but we are building with
492+
// shared memory, we need to pick a sensible upper limit.
493+
if (config->sharedMemory && maxMemory == 0) {
494+
if (ctx.isPic)
495+
maxMemory = maxMemorySetting;
496+
else
497+
maxMemory = memoryPtr;
498+
}
499+
500+
if (maxMemory != 0) {
501+
out.memorySec->maxMemoryPages = maxMemory / WasmPageSize;
498502
log("mem: max pages = " + Twine(out.memorySec->maxMemoryPages));
499503
}
500504
}

0 commit comments

Comments
 (0)