Skip to content

Commit 5f52f34

Browse files
[lld][WebAssembly] Add "--max-memory-growth"
With this option, clients (Emscripten) can implement non-growable memories without knowing the amount of initial memory upfront.
1 parent 07fd5ca commit 5f52f34

File tree

7 files changed

+81
-12
lines changed

7 files changed

+81
-12
lines changed

lld/docs/WebAssembly.rst

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

132132
Initial size of the linear memory. Default: the sum of stack, static data and heap sizes.
133133

134+
.. option:: --max-memory-growth=<value>
135+
136+
Maximum size of memory that can be allocated dynamically. Default: unlimited.
137+
134138
.. option:: --max-memory=<value>
135139

136140
Maximum size of the linear memory. Default: unlimited.

lld/test/wasm/data-layout.s

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,38 @@ 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 --max-memory-growth=0 \
107+
# RUN: -o %t_max.wasm %t.hello32.o
108+
# RUN: obj2yaml %t_max.wasm | FileCheck %s -check-prefix=CHECK-MAX-GROWTH-ZERO
109+
110+
# CHECK-MAX-GROWTH-ZERO: - Type: MEMORY
111+
# CHECK-MAX-GROWTH-ZERO-NEXT: Memories:
112+
# CHECK-MAX-GROWTH-ZERO: - Flags: [ HAS_MAX ]
113+
# CHECK-MAX-GROWTH-ZERO: Minimum: 0x5
114+
# CHECK-MAX-GROWTH-ZERO: Maximum: 0x5
115+
116+
# RUN: wasm-ld --initial-memory=196608 --max-memory-growth=262144 \
117+
# RUN: --no-entry -o %t_max.wasm %t.hello32.o
118+
# RUN: obj2yaml %t_max.wasm | FileCheck %s -check-prefix=CHECK-MAX-GROWTH-SOME
119+
120+
# CHECK-MAX-GROWTH-SOME: - Type: MEMORY
121+
# CHECK-MAX-GROWTH-SOME-NEXT: Memories:
122+
# CHECK-MAX-GROWTH-SOME: - Flags: [ HAS_MAX ]
123+
# CHECK-MAX-GROWTH-SOME: Minimum: 0x3
124+
# CHECK-MAX-GROWTH-SOME: Maximum: 0x7
125+
126+
# RUN: not wasm-ld --max-memory-growth=131073 \
127+
# RUN: --no-entry -o %t_max.wasm %t.hello32.o 2>&1 \
128+
# RUN: | FileCheck %s --check-prefix CHECK-MAX-GROWTH-ALIGN-ERROR
129+
130+
# CHECK-MAX-GROWTH-ALIGN-ERROR: maximum memory growth must be 65536-byte aligned
131+
132+
# RUN: not wasm-ld --initial-memory=131072 --max-memory=262144 --max-memory-growth=131072 \
133+
# RUN: --no-entry -o %t_max.wasm %t.hello32.o 2>&1 \
134+
# RUN: | FileCheck %s --check-prefix CHECK-MAX-GROWTH-COMPAT-ERROR
135+
136+
# CHECK-MAX-GROWTH-COMPAT-ERROR: --max-memory-growth and --max-memory are mutually exclusive
137+
106138
# RUN: wasm-ld -no-gc-sections --allow-undefined --no-entry --shared-memory \
107139
# RUN: --features=atomics,bulk-memory --initial-memory=131072 \
108140
# RUN: --max-memory=131072 -o %t_max.wasm %t32.o %t.hello32.o

lld/test/wasm/large-memory.test

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,16 @@ RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/start.s -o %
55
RUN: wasm-ld %t.o -o %t1.wasm --max-memory=2147483648
66
RUN: obj2yaml %t1.wasm | FileCheck %s --check-prefixes=CHECK,CHECK-2G
77

8+
RUN: wasm-ld %t.o -o %t1.wasm --initial-memory=131072 --max-memory-growth=2147352576
9+
RUN: obj2yaml %t1.wasm | FileCheck %s --check-prefixes=CHECK,CHECK-2G
10+
811
; And also 4G of total memory
912
RUN: wasm-ld %t.o -o %t2.wasm --max-memory=4294967296
1013
RUN: obj2yaml %t2.wasm | FileCheck %s --check-prefixes=CHECK,CHECK-4G
1114

15+
RUN: wasm-ld %t.o -o %t2.wasm --initial-memory=131072 --max-memory-growth=4294836224
16+
RUN: obj2yaml %t2.wasm | FileCheck %s --check-prefixes=CHECK,CHECK-4G
17+
1218
CHECK: - Type: MEMORY
1319
CHECK-NEXT: Memories:
1420
CHECK-NEXT: - Flags: [ HAS_MAX ]
@@ -19,6 +25,8 @@ CHECK-4G-NEXT: Maximum: 0x10000
1925
; Test error for more than 4G of memory
2026
RUN: not wasm-ld %t.o -o %t3.wasm --initial-memory=4295032832 2>&1 | FileCheck %s --check-prefix INIT-ERROR
2127
RUN: not wasm-ld %t.o -o %t4.wasm --max-memory=4295032832 2>&1 | FileCheck %s --check-prefix MAX-ERROR
28+
RUN: not wasm-ld %t.o -o %t4.wasm --initial-memory=131072 --max-memory-growth=4294901760 2>&1 | FileCheck %s --check-prefix MAX-GROWTH-ERROR
2229

2330
INIT-ERROR: initial memory too large, cannot be greater than 4294967296
2431
MAX-ERROR: maximum memory too large, cannot be greater than 4294967296
32+
MAX-GROWTH-ERROR: maximum memory growth too large, cannot be greater than 4294836224

lld/wasm/Config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ struct Configuration {
7777
uint64_t globalBase;
7878
uint64_t initialHeap;
7979
uint64_t initialMemory;
80+
uint64_t maxMemoryGrowth;
8081
uint64_t maxMemory;
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

lld/wasm/Driver.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,7 @@ static void readConfigs(opt::InputArgList &args) {
541541
config->globalBase = args::getInteger(args, OPT_global_base, 0);
542542
config->initialHeap = args::getInteger(args, OPT_initial_heap, 0);
543543
config->initialMemory = args::getInteger(args, OPT_initial_memory, 0);
544+
config->maxMemoryGrowth = args::getInteger(args, OPT_max_memory_growth, -1);
544545
config->maxMemory = args::getInteger(args, OPT_max_memory, 0);
545546
config->zStackSize =
546547
args::getZOptionValue(args, OPT_z, "stack-size", WasmPageSize);

lld/wasm/Options.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,9 @@ def initial_heap: JJ<"initial-heap=">,
227227
def initial_memory: JJ<"initial-memory=">,
228228
HelpText<"Initial size of the linear memory">;
229229

230+
def max_memory_growth: JJ<"max-memory-growth=">,
231+
HelpText<"Maximum size of dynamically allocatable linear memory">;
232+
230233
def max_memory: JJ<"max-memory=">,
231234
HelpText<"Maximum size of the linear memory">;
232235

lld/wasm/Writer.cpp

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

476+
if (config->maxMemory != 0 && config->maxMemoryGrowth != -1) {
477+
// Erroring out here is simpler than defining precedence rules.
478+
error("--max-memory-growth and --max-memory are mutually exclusive");
479+
}
480+
481+
uint64_t maxMemory = 0;
482+
if (config->maxMemoryGrowth != -1) {
483+
if (config->maxMemoryGrowth !=
484+
alignTo(config->maxMemoryGrowth, WasmPageSize))
485+
error("maximum memory growth must be " + Twine(WasmPageSize) +
486+
"-byte aligned");
487+
uint64_t maxMaxMemoryGrowth = maxMemorySetting - memoryPtr;
488+
if (config->maxMemoryGrowth > maxMaxMemoryGrowth)
489+
error("maximum memory growth too large, cannot be greater than " +
490+
Twine(maxMaxMemoryGrowth));
491+
492+
maxMemory = memoryPtr + config->maxMemoryGrowth;
493+
}
494+
476495
if (config->maxMemory != 0) {
477496
if (config->maxMemory != alignTo(config->maxMemory, WasmPageSize))
478497
error("maximum memory must be " + Twine(WasmPageSize) + "-byte aligned");
@@ -481,20 +500,21 @@ void Writer::layoutMemory() {
481500
if (config->maxMemory > maxMemorySetting)
482501
error("maximum memory too large, cannot be greater than " +
483502
Twine(maxMemorySetting));
503+
504+
maxMemory = config->maxMemory;
484505
}
485506

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;
507+
// If no maxMemory config was supplied but we are building with
508+
// shared memory, we need to pick a sensible upper limit.
509+
if (config->sharedMemory && maxMemory == 0) {
510+
if (ctx.isPic)
511+
maxMemory = maxMemorySetting;
512+
else
513+
maxMemory = memoryPtr;
514+
}
515+
516+
if (maxMemory != 0) {
517+
out.memorySec->maxMemoryPages = maxMemory / WasmPageSize;
498518
log("mem: max pages = " + Twine(out.memorySec->maxMemoryPages));
499519
}
500520
}

0 commit comments

Comments
 (0)