[lld] [lld][WebAssembly] Add `--no-growable-memory` (PR #82890)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Feb 24 13:23:09 PST 2024
https://github.com/SingleAccretion updated https://github.com/llvm/llvm-project/pull/82890
>From 5f52f345a1e18b1b26b178a3a5dd576d7c7cff9c Mon Sep 17 00:00:00 2001
From: SingleAccretion <AccretionMail at yandex.ru>
Date: Sat, 24 Feb 2024 19:51:06 +0300
Subject: [PATCH 1/2] [lld][WebAssembly] Add "--max-memory-growth"
With this option, clients (Emscripten) can implement non-growable
memories without knowing the amount of initial memory upfront.
---
lld/docs/WebAssembly.rst | 4 +++
lld/test/wasm/data-layout.s | 32 ++++++++++++++++++++++++
lld/test/wasm/large-memory.test | 8 ++++++
lld/wasm/Config.h | 1 +
lld/wasm/Driver.cpp | 1 +
lld/wasm/Options.td | 3 +++
lld/wasm/Writer.cpp | 44 ++++++++++++++++++++++++---------
7 files changed, 81 insertions(+), 12 deletions(-)
diff --git a/lld/docs/WebAssembly.rst b/lld/docs/WebAssembly.rst
index 3f554de46d38a7..ede78309ecfb1e 100644
--- a/lld/docs/WebAssembly.rst
+++ b/lld/docs/WebAssembly.rst
@@ -131,6 +131,10 @@ WebAssembly-specific options:
Initial size of the linear memory. Default: the sum of stack, static data and heap sizes.
+.. option:: --max-memory-growth=<value>
+
+ Maximum size of memory that can be allocated dynamically. Default: unlimited.
+
.. option:: --max-memory=<value>
Maximum size of the linear memory. Default: unlimited.
diff --git a/lld/test/wasm/data-layout.s b/lld/test/wasm/data-layout.s
index 2a447aad622167..6e6a6cde747e2f 100644
--- a/lld/test/wasm/data-layout.s
+++ b/lld/test/wasm/data-layout.s
@@ -103,6 +103,38 @@ local_struct_internal_ptr:
# CHECK-MAX-NEXT: Minimum: 0x2
# CHECK-MAX-NEXT: Maximum: 0x2
+# RUN: wasm-ld --no-entry --initial-memory=327680 --max-memory-growth=0 \
+# RUN: -o %t_max.wasm %t.hello32.o
+# RUN: obj2yaml %t_max.wasm | FileCheck %s -check-prefix=CHECK-MAX-GROWTH-ZERO
+
+# CHECK-MAX-GROWTH-ZERO: - Type: MEMORY
+# CHECK-MAX-GROWTH-ZERO-NEXT: Memories:
+# CHECK-MAX-GROWTH-ZERO: - Flags: [ HAS_MAX ]
+# CHECK-MAX-GROWTH-ZERO: Minimum: 0x5
+# CHECK-MAX-GROWTH-ZERO: Maximum: 0x5
+
+# RUN: wasm-ld --initial-memory=196608 --max-memory-growth=262144 \
+# RUN: --no-entry -o %t_max.wasm %t.hello32.o
+# RUN: obj2yaml %t_max.wasm | FileCheck %s -check-prefix=CHECK-MAX-GROWTH-SOME
+
+# CHECK-MAX-GROWTH-SOME: - Type: MEMORY
+# CHECK-MAX-GROWTH-SOME-NEXT: Memories:
+# CHECK-MAX-GROWTH-SOME: - Flags: [ HAS_MAX ]
+# CHECK-MAX-GROWTH-SOME: Minimum: 0x3
+# CHECK-MAX-GROWTH-SOME: Maximum: 0x7
+
+# RUN: not wasm-ld --max-memory-growth=131073 \
+# RUN: --no-entry -o %t_max.wasm %t.hello32.o 2>&1 \
+# RUN: | FileCheck %s --check-prefix CHECK-MAX-GROWTH-ALIGN-ERROR
+
+# CHECK-MAX-GROWTH-ALIGN-ERROR: maximum memory growth must be 65536-byte aligned
+
+# RUN: not wasm-ld --initial-memory=131072 --max-memory=262144 --max-memory-growth=131072 \
+# RUN: --no-entry -o %t_max.wasm %t.hello32.o 2>&1 \
+# RUN: | FileCheck %s --check-prefix CHECK-MAX-GROWTH-COMPAT-ERROR
+
+# CHECK-MAX-GROWTH-COMPAT-ERROR: --max-memory-growth and --max-memory are mutually exclusive
+
# RUN: wasm-ld -no-gc-sections --allow-undefined --no-entry --shared-memory \
# RUN: --features=atomics,bulk-memory --initial-memory=131072 \
# RUN: --max-memory=131072 -o %t_max.wasm %t32.o %t.hello32.o
diff --git a/lld/test/wasm/large-memory.test b/lld/test/wasm/large-memory.test
index 5b737e41549630..bc39a65bb3311b 100644
--- a/lld/test/wasm/large-memory.test
+++ b/lld/test/wasm/large-memory.test
@@ -5,10 +5,16 @@ RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/start.s -o %
RUN: wasm-ld %t.o -o %t1.wasm --max-memory=2147483648
RUN: obj2yaml %t1.wasm | FileCheck %s --check-prefixes=CHECK,CHECK-2G
+RUN: wasm-ld %t.o -o %t1.wasm --initial-memory=131072 --max-memory-growth=2147352576
+RUN: obj2yaml %t1.wasm | FileCheck %s --check-prefixes=CHECK,CHECK-2G
+
; And also 4G of total memory
RUN: wasm-ld %t.o -o %t2.wasm --max-memory=4294967296
RUN: obj2yaml %t2.wasm | FileCheck %s --check-prefixes=CHECK,CHECK-4G
+RUN: wasm-ld %t.o -o %t2.wasm --initial-memory=131072 --max-memory-growth=4294836224
+RUN: obj2yaml %t2.wasm | FileCheck %s --check-prefixes=CHECK,CHECK-4G
+
CHECK: - Type: MEMORY
CHECK-NEXT: Memories:
CHECK-NEXT: - Flags: [ HAS_MAX ]
@@ -19,6 +25,8 @@ CHECK-4G-NEXT: Maximum: 0x10000
; Test error for more than 4G of memory
RUN: not wasm-ld %t.o -o %t3.wasm --initial-memory=4295032832 2>&1 | FileCheck %s --check-prefix INIT-ERROR
RUN: not wasm-ld %t.o -o %t4.wasm --max-memory=4295032832 2>&1 | FileCheck %s --check-prefix MAX-ERROR
+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
INIT-ERROR: initial memory too large, cannot be greater than 4294967296
MAX-ERROR: maximum memory too large, cannot be greater than 4294967296
+MAX-GROWTH-ERROR: maximum memory growth too large, cannot be greater than 4294836224
diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h
index 97c508bda6a1c3..70872685b40120 100644
--- a/lld/wasm/Config.h
+++ b/lld/wasm/Config.h
@@ -77,6 +77,7 @@ struct Configuration {
uint64_t globalBase;
uint64_t initialHeap;
uint64_t initialMemory;
+ uint64_t maxMemoryGrowth;
uint64_t maxMemory;
// The table offset at which to place function addresses. We reserve zero
// for the null function pointer. This gets set to 1 for executables and 0
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index 635f19f78b15e6..87afdd223a707a 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -541,6 +541,7 @@ static void readConfigs(opt::InputArgList &args) {
config->globalBase = args::getInteger(args, OPT_global_base, 0);
config->initialHeap = args::getInteger(args, OPT_initial_heap, 0);
config->initialMemory = args::getInteger(args, OPT_initial_memory, 0);
+ config->maxMemoryGrowth = args::getInteger(args, OPT_max_memory_growth, -1);
config->maxMemory = args::getInteger(args, OPT_max_memory, 0);
config->zStackSize =
args::getZOptionValue(args, OPT_z, "stack-size", WasmPageSize);
diff --git a/lld/wasm/Options.td b/lld/wasm/Options.td
index 8190717cef63bb..ed3e014850278a 100644
--- a/lld/wasm/Options.td
+++ b/lld/wasm/Options.td
@@ -227,6 +227,9 @@ def initial_heap: JJ<"initial-heap=">,
def initial_memory: JJ<"initial-memory=">,
HelpText<"Initial size of the linear memory">;
+def max_memory_growth: JJ<"max-memory-growth=">,
+ HelpText<"Maximum size of dynamically allocatable linear memory">;
+
def max_memory: JJ<"max-memory=">,
HelpText<"Maximum size of the linear memory">;
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index d1a06c9ac9c2ae..261d633f11b745 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -473,6 +473,25 @@ void Writer::layoutMemory() {
WasmSym::heapEnd->setVA(memoryPtr);
}
+ if (config->maxMemory != 0 && config->maxMemoryGrowth != -1) {
+ // Erroring out here is simpler than defining precedence rules.
+ error("--max-memory-growth and --max-memory are mutually exclusive");
+ }
+
+ uint64_t maxMemory = 0;
+ if (config->maxMemoryGrowth != -1) {
+ if (config->maxMemoryGrowth !=
+ alignTo(config->maxMemoryGrowth, WasmPageSize))
+ error("maximum memory growth must be " + Twine(WasmPageSize) +
+ "-byte aligned");
+ uint64_t maxMaxMemoryGrowth = maxMemorySetting - memoryPtr;
+ if (config->maxMemoryGrowth > maxMaxMemoryGrowth)
+ error("maximum memory growth too large, cannot be greater than " +
+ Twine(maxMaxMemoryGrowth));
+
+ maxMemory = memoryPtr + config->maxMemoryGrowth;
+ }
+
if (config->maxMemory != 0) {
if (config->maxMemory != alignTo(config->maxMemory, WasmPageSize))
error("maximum memory must be " + Twine(WasmPageSize) + "-byte aligned");
@@ -481,20 +500,21 @@ void Writer::layoutMemory() {
if (config->maxMemory > maxMemorySetting)
error("maximum memory too large, cannot be greater than " +
Twine(maxMemorySetting));
+
+ maxMemory = config->maxMemory;
}
- // Check max if explicitly supplied or required by shared memory
- if (config->maxMemory != 0 || config->sharedMemory) {
- uint64_t max = config->maxMemory;
- if (max == 0) {
- // If no maxMemory config was supplied but we are building with
- // shared memory, we need to pick a sensible upper limit.
- if (ctx.isPic)
- max = maxMemorySetting;
- else
- max = memoryPtr;
- }
- out.memorySec->maxMemoryPages = max / WasmPageSize;
+ // If no maxMemory config was supplied but we are building with
+ // shared memory, we need to pick a sensible upper limit.
+ if (config->sharedMemory && maxMemory == 0) {
+ if (ctx.isPic)
+ maxMemory = maxMemorySetting;
+ else
+ maxMemory = memoryPtr;
+ }
+
+ if (maxMemory != 0) {
+ out.memorySec->maxMemoryPages = maxMemory / WasmPageSize;
log("mem: max pages = " + Twine(out.memorySec->maxMemoryPages));
}
}
>From f6e6499c5f309da9bbcb902f2917c0bb6fa8aab2 Mon Sep 17 00:00:00 2001
From: SingleAccretion <AccretionMail at yandex.ru>
Date: Sun, 25 Feb 2024 00:18:34 +0300
Subject: [PATCH 2/2] Add --no-growable-memory instead
---
lld/docs/WebAssembly.rst | 8 ++++----
lld/test/wasm/data-layout.s | 36 +++++++++------------------------
lld/test/wasm/large-memory.test | 8 --------
lld/wasm/Config.h | 2 +-
lld/wasm/Driver.cpp | 2 +-
lld/wasm/Options.td | 6 +++---
lld/wasm/Writer.cpp | 21 ++++++-------------
7 files changed, 25 insertions(+), 58 deletions(-)
diff --git a/lld/docs/WebAssembly.rst b/lld/docs/WebAssembly.rst
index ede78309ecfb1e..1dd05d67983c7d 100644
--- a/lld/docs/WebAssembly.rst
+++ b/lld/docs/WebAssembly.rst
@@ -131,14 +131,14 @@ WebAssembly-specific options:
Initial size of the linear memory. Default: the sum of stack, static data and heap sizes.
-.. option:: --max-memory-growth=<value>
-
- Maximum size of memory that can be allocated dynamically. Default: unlimited.
-
.. option:: --max-memory=<value>
Maximum size of the linear memory. Default: unlimited.
+.. option:: --no-growable-memory
+
+ Set maximum size of the linear memory to its initial size, disallowing memory growth.
+
By default the function table is neither imported nor exported, but defined
for internal use only.
diff --git a/lld/test/wasm/data-layout.s b/lld/test/wasm/data-layout.s
index 6e6a6cde747e2f..7f9da2e5387cac 100644
--- a/lld/test/wasm/data-layout.s
+++ b/lld/test/wasm/data-layout.s
@@ -103,37 +103,21 @@ local_struct_internal_ptr:
# CHECK-MAX-NEXT: Minimum: 0x2
# CHECK-MAX-NEXT: Maximum: 0x2
-# RUN: wasm-ld --no-entry --initial-memory=327680 --max-memory-growth=0 \
+# RUN: wasm-ld --no-entry --initial-memory=327680 --no-growable-memory \
# RUN: -o %t_max.wasm %t.hello32.o
-# RUN: obj2yaml %t_max.wasm | FileCheck %s -check-prefix=CHECK-MAX-GROWTH-ZERO
+# RUN: obj2yaml %t_max.wasm | FileCheck %s -check-prefix=CHECK-NO-GROWTH
-# CHECK-MAX-GROWTH-ZERO: - Type: MEMORY
-# CHECK-MAX-GROWTH-ZERO-NEXT: Memories:
-# CHECK-MAX-GROWTH-ZERO: - Flags: [ HAS_MAX ]
-# CHECK-MAX-GROWTH-ZERO: Minimum: 0x5
-# CHECK-MAX-GROWTH-ZERO: Maximum: 0x5
+# CHECK-NO-GROWTH: - Type: MEMORY
+# CHECK-NO-GROWTH-NEXT: Memories:
+# CHECK-NO-GROWTH-NEXT: - Flags: [ HAS_MAX ]
+# CHECK-NO-GROWTH-NEXT: Minimum: 0x5
+# CHECK-NO-GROWTH-NEXT: Maximum: 0x5
-# RUN: wasm-ld --initial-memory=196608 --max-memory-growth=262144 \
-# RUN: --no-entry -o %t_max.wasm %t.hello32.o
-# RUN: obj2yaml %t_max.wasm | FileCheck %s -check-prefix=CHECK-MAX-GROWTH-SOME
-
-# CHECK-MAX-GROWTH-SOME: - Type: MEMORY
-# CHECK-MAX-GROWTH-SOME-NEXT: Memories:
-# CHECK-MAX-GROWTH-SOME: - Flags: [ HAS_MAX ]
-# CHECK-MAX-GROWTH-SOME: Minimum: 0x3
-# CHECK-MAX-GROWTH-SOME: Maximum: 0x7
-
-# RUN: not wasm-ld --max-memory-growth=131073 \
-# RUN: --no-entry -o %t_max.wasm %t.hello32.o 2>&1 \
-# RUN: | FileCheck %s --check-prefix CHECK-MAX-GROWTH-ALIGN-ERROR
-
-# CHECK-MAX-GROWTH-ALIGN-ERROR: maximum memory growth must be 65536-byte aligned
-
-# RUN: not wasm-ld --initial-memory=131072 --max-memory=262144 --max-memory-growth=131072 \
+# RUN: not wasm-ld --max-memory=262144 --no-growable-memory \
# RUN: --no-entry -o %t_max.wasm %t.hello32.o 2>&1 \
-# RUN: | FileCheck %s --check-prefix CHECK-MAX-GROWTH-COMPAT-ERROR
+# RUN: | FileCheck %s --check-prefix CHECK-NO-GROWTH-COMPAT-ERROR
-# CHECK-MAX-GROWTH-COMPAT-ERROR: --max-memory-growth and --max-memory are mutually exclusive
+# CHECK-NO-GROWTH-COMPAT-ERROR: --max-memory and --no-growable-memory are mutually exclusive
# RUN: wasm-ld -no-gc-sections --allow-undefined --no-entry --shared-memory \
# RUN: --features=atomics,bulk-memory --initial-memory=131072 \
diff --git a/lld/test/wasm/large-memory.test b/lld/test/wasm/large-memory.test
index bc39a65bb3311b..5b737e41549630 100644
--- a/lld/test/wasm/large-memory.test
+++ b/lld/test/wasm/large-memory.test
@@ -5,16 +5,10 @@ RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/start.s -o %
RUN: wasm-ld %t.o -o %t1.wasm --max-memory=2147483648
RUN: obj2yaml %t1.wasm | FileCheck %s --check-prefixes=CHECK,CHECK-2G
-RUN: wasm-ld %t.o -o %t1.wasm --initial-memory=131072 --max-memory-growth=2147352576
-RUN: obj2yaml %t1.wasm | FileCheck %s --check-prefixes=CHECK,CHECK-2G
-
; And also 4G of total memory
RUN: wasm-ld %t.o -o %t2.wasm --max-memory=4294967296
RUN: obj2yaml %t2.wasm | FileCheck %s --check-prefixes=CHECK,CHECK-4G
-RUN: wasm-ld %t.o -o %t2.wasm --initial-memory=131072 --max-memory-growth=4294836224
-RUN: obj2yaml %t2.wasm | FileCheck %s --check-prefixes=CHECK,CHECK-4G
-
CHECK: - Type: MEMORY
CHECK-NEXT: Memories:
CHECK-NEXT: - Flags: [ HAS_MAX ]
@@ -25,8 +19,6 @@ CHECK-4G-NEXT: Maximum: 0x10000
; Test error for more than 4G of memory
RUN: not wasm-ld %t.o -o %t3.wasm --initial-memory=4295032832 2>&1 | FileCheck %s --check-prefix INIT-ERROR
RUN: not wasm-ld %t.o -o %t4.wasm --max-memory=4295032832 2>&1 | FileCheck %s --check-prefix MAX-ERROR
-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
INIT-ERROR: initial memory too large, cannot be greater than 4294967296
MAX-ERROR: maximum memory too large, cannot be greater than 4294967296
-MAX-GROWTH-ERROR: maximum memory growth too large, cannot be greater than 4294836224
diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h
index 70872685b40120..266348fef40316 100644
--- a/lld/wasm/Config.h
+++ b/lld/wasm/Config.h
@@ -77,8 +77,8 @@ struct Configuration {
uint64_t globalBase;
uint64_t initialHeap;
uint64_t initialMemory;
- uint64_t maxMemoryGrowth;
uint64_t maxMemory;
+ bool noGrowableMemory;
// The table offset at which to place function addresses. We reserve zero
// for the null function pointer. This gets set to 1 for executables and 0
// for shared libraries (since they always added to a dynamic offset at
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index 87afdd223a707a..94b9f4d799f31e 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -541,8 +541,8 @@ static void readConfigs(opt::InputArgList &args) {
config->globalBase = args::getInteger(args, OPT_global_base, 0);
config->initialHeap = args::getInteger(args, OPT_initial_heap, 0);
config->initialMemory = args::getInteger(args, OPT_initial_memory, 0);
- config->maxMemoryGrowth = args::getInteger(args, OPT_max_memory_growth, -1);
config->maxMemory = args::getInteger(args, OPT_max_memory, 0);
+ config->noGrowableMemory = args.hasArg(OPT_no_growable_memory);
config->zStackSize =
args::getZOptionValue(args, OPT_z, "stack-size", WasmPageSize);
diff --git a/lld/wasm/Options.td b/lld/wasm/Options.td
index ed3e014850278a..70b5aadc26c2a0 100644
--- a/lld/wasm/Options.td
+++ b/lld/wasm/Options.td
@@ -227,12 +227,12 @@ def initial_heap: JJ<"initial-heap=">,
def initial_memory: JJ<"initial-memory=">,
HelpText<"Initial size of the linear memory">;
-def max_memory_growth: JJ<"max-memory-growth=">,
- HelpText<"Maximum size of dynamically allocatable linear memory">;
-
def max_memory: JJ<"max-memory=">,
HelpText<"Maximum size of the linear memory">;
+def no_growable_memory: FF<"no-growable-memory">,
+ HelpText<"Set maximum size of the linear memory to its initial size">;
+
def no_entry: FF<"no-entry">,
HelpText<"Do not output any entry point">;
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 261d633f11b745..67dcb951e8b0d2 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -473,25 +473,12 @@ void Writer::layoutMemory() {
WasmSym::heapEnd->setVA(memoryPtr);
}
- if (config->maxMemory != 0 && config->maxMemoryGrowth != -1) {
+ if (config->maxMemory != 0 && config->noGrowableMemory) {
// Erroring out here is simpler than defining precedence rules.
- error("--max-memory-growth and --max-memory are mutually exclusive");
+ error("--max-memory and --no-growable-memory are mutually exclusive");
}
uint64_t maxMemory = 0;
- if (config->maxMemoryGrowth != -1) {
- if (config->maxMemoryGrowth !=
- alignTo(config->maxMemoryGrowth, WasmPageSize))
- error("maximum memory growth must be " + Twine(WasmPageSize) +
- "-byte aligned");
- uint64_t maxMaxMemoryGrowth = maxMemorySetting - memoryPtr;
- if (config->maxMemoryGrowth > maxMaxMemoryGrowth)
- error("maximum memory growth too large, cannot be greater than " +
- Twine(maxMaxMemoryGrowth));
-
- maxMemory = memoryPtr + config->maxMemoryGrowth;
- }
-
if (config->maxMemory != 0) {
if (config->maxMemory != alignTo(config->maxMemory, WasmPageSize))
error("maximum memory must be " + Twine(WasmPageSize) + "-byte aligned");
@@ -503,6 +490,10 @@ void Writer::layoutMemory() {
maxMemory = config->maxMemory;
}
+ else if (config->noGrowableMemory)
+ {
+ maxMemory = memoryPtr;
+ }
// If no maxMemory config was supplied but we are building with
// shared memory, we need to pick a sensible upper limit.
More information about the llvm-commits
mailing list