Skip to content

Commit ebc7eb4

Browse files
committed
[ldd][WebAssembly] Add support for the custom-page-sizes proposal
This commit adds support for WebAssembly's custom-page-sizes proposal to `wasm-ld`. An overview of the proposal can be found [here](https://github.com/WebAssembly/custom-page-sizes/blob/main/proposals/custom-page-sizes/Overview.md). In a sentence, it allows customizing a Wasm memory's page size, enabling Wasm to target environments with less than 64KiB of memory (the default WebAssembly page size) available for Wasm memories. This commit contains the following: * Adds a `--page-size=N` CLI flag to `wasm-ld` for configuring the linked Wasm binary's linear memory's page size. * When the page size is configured to a non-default value, then the final Wasm binary will use the encodings defined in the custom-page-sizes proposal to declare the linear memory's page size. * Defines a `__wasm_first_page_end` symbol, whose address points to the first page in the Wasm linear memory, a.k.a. is the Wasm memory's page size. This allows writing code that is compatible with any page size, and doesn't require re-compiling its object code. At the same time, because it just lowers to a constant rather than a memory access or something, it enables link-time optimization. * Adds tests for these new features.
1 parent a36a67c commit ebc7eb4

23 files changed

+116
-25
lines changed

lld/test/wasm/initial-heap.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ RUN: not wasm-ld %t.o -o %t5.wasm --stack-first -z stack-size=131072 --initial-h
2020
RUN: not wasm-ld %t.o -o %t6.wasm --stack-first -z stack-size=65536 --initial-heap=131072 --initial-memory=131072 2>&1 | FileCheck %s --check-prefix INITIAL-MEMORY-TOO-SMALL
2121
RUN: not wasm-ld %t.o -o %t7.wasm --stack-first -z stack-size=65536 --initial-heap=131072 --max-memory=131072 2>&1 | FileCheck %s --check-prefix MAX-MEMORY-TOO-SMALL
2222

23-
NOT-PAGE-MULTIPLE: initial heap must be 65536-byte aligned
23+
NOT-PAGE-MULTIPLE: initial heap must be aligned to the page size (65536 bytes)
2424
TOO-LARGE-BY-ITSELF: initial heap too large, cannot be greater than 4294901760
2525
TOO-LARGE-WITH-STACK: initial heap too large, cannot be greater than 4294836224
2626
INITIAL-MEMORY-TOO-SMALL: initial memory too small, 196608 bytes needed

lld/test/wasm/mutable-global-exports.s

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ _start:
101101
# CHECK-ALL-NEXT: - Name: __table_base
102102
# CHECK-ALL-NEXT: Kind: GLOBAL
103103
# CHECK-ALL-NEXT: Index: 10
104+
# CHECK-ALL-NEXT: - Name: __wasm_first_page_end
105+
# CHECK-ALL-NEXT: Kind: GLOBAL
106+
# CHECK-ALL-NEXT: Index: 11
104107
# CHECK-ALL-NEXT: - Type: CODE
105108

106109
# CHECK-ALL: Name: target_features
@@ -110,4 +113,3 @@ _start:
110113
# CHECK-ALL-NEXT: - Prefix: USED
111114
# CHECK-ALL-NEXT: Name: mutable-globals
112115
# CHECK-ALL-NEXT: ...
113-

lld/test/wasm/page-size.s

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %s -o %t.o
2+
3+
.globl _start
4+
_start:
5+
.functype _start () -> (i32)
6+
i32.const __wasm_first_page_end
7+
end_function
8+
9+
# Add a symbol to smoke test that `__wasm_first_page_end` is absolute and not
10+
# relative to other data.
11+
.section .data.foo,"",@
12+
foo:
13+
.int32 0x11111111
14+
.size foo, 4
15+
16+
# RUN: wasm-ld -no-gc-sections -o %t.custom.wasm %t.o --page-size=1
17+
# RUN: obj2yaml %t.custom.wasm | FileCheck %s --check-prefix=CHECK-CUSTOM
18+
19+
# CHECK-CUSTOM: - Type: MEMORY
20+
# CHECK-CUSTOM-NEXT: Memories:
21+
# CHECK-CUSTOM-NEXT: - Flags: [ HAS_PAGE_SIZE ]
22+
# CHECK-CUSTOM-NEXT: Minimum: 0x10410
23+
# CHECK-CUSTOM-NEXT: PageSize: 0x1
24+
25+
# RUN: llvm-objdump --disassemble-symbols=_start %t.custom.wasm | FileCheck %s --check-prefix=CHECK-CUSTOM-DIS
26+
27+
# CHECK-CUSTOM-DIS: <_start>:
28+
# CHECK-CUSTOM-DIS: i32.const 1
29+
# CHECK-CUSTOM-DIS-NEXT: end
30+
31+
# RUN: wasm-ld -no-gc-sections -o %t.default.wasm %t.o
32+
# RUN: obj2yaml %t.default.wasm | FileCheck %s --check-prefix=CHECK-DEFAULT
33+
34+
# CHECK-DEFAULT: - Type: MEMORY
35+
# CHECK-DEFAULT-NEXT: Memories:
36+
# CHECK-DEFAULT-NEXT: Minimum: 0x2
37+
# CHECK-DEFAULT-NEXT: - Type: GLOBAL
38+
39+
# RUN: llvm-objdump --disassemble-symbols=_start %t.default.wasm | FileCheck %s --check-prefix=CHECK-DEFAULT-DIS
40+
41+
# CHECK-DEFAULT-DIS: <_start>:
42+
# CHECK-DEFAULT-DIS: i32.const 65536
43+
# CHECK-DEFAULT-DIS-NEXT: end

lld/test/wasm/shared-memory.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Sections:
5656
Flags: [ ]
5757
...
5858

59-
# SHARED-UNALIGNED: maximum memory must be 65536-byte aligned{{$}}
59+
# SHARED-UNALIGNED: maximum memory must be aligned to the page size (65536 bytes)
6060

6161
# SHARED-NO-ATOMICS: 'atomics' feature must be used in order to use shared memory
6262

lld/wasm/Config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ struct Config {
9494
// runtime).
9595
uint64_t tableBase;
9696
uint64_t zStackSize;
97+
uint64_t pageSize;
9798
unsigned ltoPartitions;
9899
unsigned ltoo;
99100
llvm::CodeGenOptLevel ltoCgo;

lld/wasm/Driver.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,10 @@ static void readConfigs(opt::InputArgList &args) {
643643
ctx.arg.maxMemory = args::getInteger(args, OPT_max_memory, 0);
644644
ctx.arg.noGrowableMemory = args.hasArg(OPT_no_growable_memory);
645645
ctx.arg.zStackSize =
646-
args::getZOptionValue(args, OPT_z, "stack-size", WasmPageSize);
646+
args::getZOptionValue(args, OPT_z, "stack-size", WasmDefaultPageSize);
647+
ctx.arg.pageSize = args::getInteger(args, OPT_page_size, WasmDefaultPageSize);
648+
if (ctx.arg.pageSize != 1 && ctx.arg.pageSize != WasmDefaultPageSize)
649+
error("--page_size=N must be either 1 or 65536");
647650

648651
// -Bdynamic by default if -pie or -shared is specified.
649652
if (ctx.arg.pie || ctx.arg.shared)
@@ -1000,6 +1003,11 @@ static void createOptionalSymbols() {
10001003
WasmSym::definedTableBase = symtab->addOptionalDataSymbol("__table_base");
10011004
}
10021005

1006+
WasmSym::firstPageEnd =
1007+
symtab->addOptionalDataSymbol("__wasm_first_page_end");
1008+
if (WasmSym::firstPageEnd)
1009+
WasmSym::firstPageEnd->setVA(ctx.arg.pageSize);
1010+
10031011
// For non-shared memory programs we still need to define __tls_base since we
10041012
// allow object files built with TLS to be linked into single threaded
10051013
// programs, and such object files can contain references to this symbol.

lld/wasm/Options.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,9 @@ def import_table: FF<"import-table">,
232232
def initial_heap: JJ<"initial-heap=">,
233233
HelpText<"Initial size of the heap">;
234234

235+
def page_size: JJ<"page-size=">,
236+
HelpText<"The Wasm page size (Defaults to 65536)">;
237+
235238
def initial_memory: JJ<"initial-memory=">,
236239
HelpText<"Initial size of the linear memory">;
237240

lld/wasm/SymbolTable.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -792,7 +792,7 @@ Symbol *SymbolTable::addUndefinedTag(StringRef name,
792792
}
793793

794794
TableSymbol *SymbolTable::createUndefinedIndirectFunctionTable(StringRef name) {
795-
WasmLimits limits{0, 0, 0}; // Set by the writer.
795+
WasmLimits limits{0, 0, 0, 0}; // Set by the writer.
796796
WasmTableType *type = make<WasmTableType>();
797797
type->ElemType = ValType::FUNCREF;
798798
type->Limits = limits;
@@ -807,7 +807,7 @@ TableSymbol *SymbolTable::createUndefinedIndirectFunctionTable(StringRef name) {
807807

808808
TableSymbol *SymbolTable::createDefinedIndirectFunctionTable(StringRef name) {
809809
const uint32_t invalidIndex = -1;
810-
WasmLimits limits{0, 0, 0}; // Set by the writer.
810+
WasmLimits limits{0, 0, 0, 0}; // Set by the writer.
811811
WasmTableType type{ValType::FUNCREF, limits};
812812
WasmTable desc{invalidIndex, type, name};
813813
InputTable *table = make<InputTable>(desc, nullptr);

lld/wasm/Symbols.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ DefinedFunction *WasmSym::applyGlobalRelocs;
8484
DefinedFunction *WasmSym::applyTLSRelocs;
8585
DefinedFunction *WasmSym::applyGlobalTLSRelocs;
8686
DefinedFunction *WasmSym::initTLS;
87+
DefinedData *WasmSym::firstPageEnd;
8788
DefinedFunction *WasmSym::startFunction;
8889
DefinedData *WasmSym::dsoHandle;
8990
DefinedData *WasmSym::dataEnd;

lld/wasm/Symbols.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,10 @@ struct WasmSym {
577577
static DefinedData *heapBase;
578578
static DefinedData *heapEnd;
579579

580+
// __wasm_first_page_end
581+
// A symbol whose address is the end of the first page in memory (if any).
582+
static DefinedData *firstPageEnd;
583+
580584
// __wasm_init_memory_flag
581585
// Symbol whose contents are nonzero iff memory has already been initialized.
582586
static DefinedData *initMemoryFlag;

0 commit comments

Comments
 (0)