Skip to content

Commit 03fdc56

Browse files
fitzgentstellar
authored andcommitted
[lld][WebAssembly] Support for the custom-page-sizes WebAssembly proposal (#128942)
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 Wasm 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. r? @sbc100 cc @sunfishcode (cherry picked from commit 6018930)
1 parent 63e63f3 commit 03fdc56

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
@@ -642,7 +642,10 @@ static void readConfigs(opt::InputArgList &args) {
642642
ctx.arg.maxMemory = args::getInteger(args, OPT_max_memory, 0);
643643
ctx.arg.noGrowableMemory = args.hasArg(OPT_no_growable_memory);
644644
ctx.arg.zStackSize =
645-
args::getZOptionValue(args, OPT_z, "stack-size", WasmPageSize);
645+
args::getZOptionValue(args, OPT_z, "stack-size", WasmDefaultPageSize);
646+
ctx.arg.pageSize = args::getInteger(args, OPT_page_size, WasmDefaultPageSize);
647+
if (ctx.arg.pageSize != 1 && ctx.arg.pageSize != WasmDefaultPageSize)
648+
error("--page_size=N must be either 1 or 65536");
646649

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

1005+
WasmSym::firstPageEnd =
1006+
symtab->addOptionalDataSymbol("__wasm_first_page_end");
1007+
if (WasmSym::firstPageEnd)
1008+
WasmSym::firstPageEnd->setVA(ctx.arg.pageSize);
1009+
10021010
// For non-shared memory programs we still need to define __tls_base since we
10031011
// allow object files built with TLS to be linked into single threaded
10041012
// 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
@@ -230,6 +230,9 @@ def import_table: FF<"import-table">,
230230
def initial_heap: JJ<"initial-heap=">,
231231
HelpText<"Initial size of the heap">;
232232

233+
def page_size: JJ<"page-size=">,
234+
HelpText<"The Wasm page size (Defaults to 65536)">;
235+
233236
def initial_memory: JJ<"initial-memory=">,
234237
HelpText<"Initial size of the linear memory">;
235238

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)