From 5f52f345a1e18b1b26b178a3a5dd576d7c7cff9c Mon Sep 17 00:00:00 2001 From: SingleAccretion Date: Sat, 24 Feb 2024 19:51:06 +0300 Subject: [PATCH 1/3] [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 3f554de46d38a..ede78309ecfb1 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= + + Maximum size of memory that can be allocated dynamically. Default: unlimited. + .. option:: --max-memory= 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 2a447aad62216..6e6a6cde747e2 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 5b737e4154963..bc39a65bb3311 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 97c508bda6a1c..70872685b4012 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 635f19f78b15e..87afdd223a707 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 8190717cef63b..ed3e014850278 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 d1a06c9ac9c2a..261d633f11b74 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 9bf7075d788ad3c8dbb664976f376450c299a8b9 Mon Sep 17 00:00:00 2001 From: SingleAccretion Date: Sun, 25 Feb 2024 00:18:34 +0300 Subject: [PATCH 2/3] 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 | 19 ++++------------- 7 files changed, 23 insertions(+), 58 deletions(-) diff --git a/lld/docs/WebAssembly.rst b/lld/docs/WebAssembly.rst index ede78309ecfb1..1dd05d67983c7 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= - - Maximum size of memory that can be allocated dynamically. Default: unlimited. - .. option:: --max-memory= 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 6e6a6cde747e2..7f9da2e5387ca 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 bc39a65bb3311..5b737e4154963 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 70872685b4012..266348fef4031 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 87afdd223a707..94b9f4d799f31 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 ed3e014850278..70b5aadc26c2a 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 261d633f11b74..94832a4ca4170 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"); @@ -502,6 +489,8 @@ void Writer::layoutMemory() { Twine(maxMemorySetting)); maxMemory = config->maxMemory; + } else if (config->noGrowableMemory) { + maxMemory = memoryPtr; } // If no maxMemory config was supplied but we are building with From bed9560457cfb98cefd402bd836e15f1a99952c5 Mon Sep 17 00:00:00 2001 From: SingleAccretion Date: Sun, 25 Feb 2024 12:18:25 +0300 Subject: [PATCH 3/3] Tweak error handling --- lld/test/wasm/data-layout.s | 2 +- lld/wasm/Driver.cpp | 5 +++++ lld/wasm/Writer.cpp | 5 ----- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lld/test/wasm/data-layout.s b/lld/test/wasm/data-layout.s index 7f9da2e5387ca..a68bc032e4840 100644 --- a/lld/test/wasm/data-layout.s +++ b/lld/test/wasm/data-layout.s @@ -117,7 +117,7 @@ local_struct_internal_ptr: # RUN: --no-entry -o %t_max.wasm %t.hello32.o 2>&1 \ # RUN: | FileCheck %s --check-prefix CHECK-NO-GROWTH-COMPAT-ERROR -# CHECK-NO-GROWTH-COMPAT-ERROR: --max-memory and --no-growable-memory are mutually exclusive +# CHECK-NO-GROWTH-COMPAT-ERROR: --max-memory is incompatible with --no-growable-memory # RUN: wasm-ld -no-gc-sections --allow-undefined --no-entry --shared-memory \ # RUN: --features=atomics,bulk-memory --initial-memory=131072 \ diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp index 94b9f4d799f31..df7d4d1cc3d67 100644 --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -546,6 +546,11 @@ static void readConfigs(opt::InputArgList &args) { config->zStackSize = args::getZOptionValue(args, OPT_z, "stack-size", WasmPageSize); + if (config->maxMemory != 0 && config->noGrowableMemory) { + // Erroring out here is simpler than defining precedence rules. + error("--max-memory is incompatible with --no-growable-memory"); + } + // Default value of exportDynamic depends on `-shared` config->exportDynamic = args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, config->shared); diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp index 94832a4ca4170..55eff995fb8a1 100644 --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -473,11 +473,6 @@ void Writer::layoutMemory() { WasmSym::heapEnd->setVA(memoryPtr); } - if (config->maxMemory != 0 && config->noGrowableMemory) { - // Erroring out here is simpler than defining precedence rules. - error("--max-memory and --no-growable-memory are mutually exclusive"); - } - uint64_t maxMemory = 0; if (config->maxMemory != 0) { if (config->maxMemory != alignTo(config->maxMemory, WasmPageSize))