Skip to content
This repository was archived by the owner on Oct 3, 2025. It is now read-only.

Commit 3c16adc

Browse files
feat: Custom memory page size (#22)
1 parent ba6a03d commit 3c16adc

File tree

18 files changed

+378
-63
lines changed

18 files changed

+378
-63
lines changed

Cargo.lock

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,18 @@ TinyWasm passes all WebAssembly MVP tests from the [WebAssembly core testsuite](
2727
🚧 -- in development / partialy supported\
2828
🟢 -- fully supported
2929

30-
| Proposal | Status | TinyWasm Version |
31-
| -------------------------------------------------------------------------------------------------------------------------- | ------ | ---------------- |
32-
| [**Mutable Globals**](https://github.com/WebAssembly/mutable-global/blob/master/proposals/mutable-global/Overview.md) | 🟢 | 0.2.0 |
33-
| [**Non-trapping float-to-int Conversion**](https://github.com/WebAssembly/nontrapping-float-to-int-conversions) | 🟢 | 0.2.0 |
34-
| [**Sign-extension operators**](https://github.com/WebAssembly/sign-extension-ops) | 🟢 | 0.2.0 |
35-
| [**Multi-value**](https://github.com/WebAssembly/spec/blob/master/proposals/multi-value/Overview.md) | 🟢 | 0.2.0 |
36-
| [**Bulk Memory Operations**](https://github.com/WebAssembly/spec/blob/master/proposals/bulk-memory-operations/Overview.md) | 🟢 | 0.4.0 |
37-
| [**Reference Types**](https://github.com/WebAssembly/reference-types/blob/master/proposals/reference-types/Overview.md) | 🟢 | 0.7.0 |
38-
| [**Multiple Memories**](https://github.com/WebAssembly/multi-memory/blob/master/proposals/multi-memory/Overview.md) | 🟢 | 0.8.0 |
39-
| [**Memory64**](https://github.com/WebAssembly/memory64/blob/master/proposals/memory64/Overview.md) | 🚧 | N/A |
40-
| [**Fixed-Width SIMD**](https://github.com/webassembly/simd) | 🌑 | N/A |
30+
| Proposal | Status | TinyWasm Version |
31+
| --------------------------------------------------------------------------------------------------------------------------- | ------ | ---------------- |
32+
| [**Mutable Globals**](https://github.com/WebAssembly/mutable-global/blob/master/proposals/mutable-global/Overview.md) | 🟢 | 0.2.0 |
33+
| [**Non-trapping float-to-int Conversion**](https://github.com/WebAssembly/nontrapping-float-to-int-conversions) | 🟢 | 0.2.0 |
34+
| [**Sign-extension operators**](https://github.com/WebAssembly/sign-extension-ops) | 🟢 | 0.2.0 |
35+
| [**Multi-value**](https://github.com/WebAssembly/spec/blob/master/proposals/multi-value/Overview.md) | 🟢 | 0.2.0 |
36+
| [**Bulk Memory Operations**](https://github.com/WebAssembly/spec/blob/master/proposals/bulk-memory-operations/Overview.md) | 🟢 | 0.4.0 |
37+
| [**Reference Types**](https://github.com/WebAssembly/reference-types/blob/master/proposals/reference-types/Overview.md) | 🟢 | 0.7.0 |
38+
| [**Multiple Memories**](https://github.com/WebAssembly/multi-memory/blob/master/proposals/multi-memory/Overview.md) | 🟢 | 0.8.0 |
39+
| [**Custom Page Sizes**](https://github.com/WebAssembly/custom-page-sizes/blob/main/proposals/custom-page-sizes/Overview.md) | 🟢 | `next` |
40+
| [**Memory64**](https://github.com/WebAssembly/memory64/blob/master/proposals/memory64/Overview.md) | 🚧 | N/A |
41+
| [**Fixed-Width SIMD**](https://github.com/webassembly/simd) | 🌑 | N/A |
4142

4243
## Usage
4344

crates/parser/src/conversion.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,12 @@ pub(crate) fn convert_module_memories<T: IntoIterator<Item = wasmparser::Result<
105105
}
106106

107107
pub(crate) fn convert_module_memory(memory: wasmparser::MemoryType) -> MemoryType {
108-
MemoryType {
109-
arch: if memory.memory64 { MemoryArch::I64 } else { MemoryArch::I32 },
110-
page_count_initial: memory.initial,
111-
page_count_max: memory.maximum,
112-
}
108+
MemoryType::new(
109+
if memory.memory64 { MemoryArch::I64 } else { MemoryArch::I32 },
110+
memory.initial,
111+
memory.maximum,
112+
memory.page_size_log2.map(|x| 1 << x),
113+
)
113114
}
114115

115116
pub(crate) fn convert_module_tables<'a, T: IntoIterator<Item = wasmparser::Result<wasmparser::Table<'a>>>>(

crates/parser/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ impl Parser {
6363
multi_memory: true,
6464
memory64: false,
6565
simd: true,
66+
custom_page_sizes: true,
6667

6768
gc_types: true,
6869
component_model: false,
@@ -75,7 +76,6 @@ impl Parser {
7576
memory_control: false,
7677
relaxed_simd: false,
7778
threads: false,
78-
custom_page_sizes: false,
7979
shared_everything_threads: false,
8080
component_model_multiple_returns: false,
8181
legacy_exceptions: false,

crates/tinywasm/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ name="test-wasm-simd"
7373
harness=false
7474
test=false
7575

76+
77+
[[test]]
78+
name="test-wasm-custom-page-sizes"
79+
harness=false
80+
test=false
81+
7682
[[test]]
7783
name="test-wast"
7884
harness=false

crates/tinywasm/benches/argon2id.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use criterion::{criterion_group, criterion_main, Criterion};
22
use eyre::Result;
3-
use tinywasm::{ModuleInstance, Store, types};
3+
use tinywasm::{types, ModuleInstance, Store};
44
use types::{archive::AlignedVec, TinyWasmModule};
55

66
const WASM: &[u8] = include_bytes!("../../../examples/rust/out/argon2id.opt.wasm");

crates/tinywasm/benches/fibonacci.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use criterion::{criterion_group, criterion_main, Criterion};
22
use eyre::Result;
3-
use tinywasm::{ModuleInstance, Store, types};
3+
use tinywasm::{types, ModuleInstance, Store};
44
use types::{archive::AlignedVec, TinyWasmModule};
55

66
const WASM: &[u8] = include_bytes!("../../../examples/rust/out/fibonacci.opt.wasm");

crates/tinywasm/benches/tinywasm.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use criterion::{criterion_group, criterion_main, Criterion};
22
use eyre::Result;
3-
use tinywasm::{Extern, FuncContext, Imports, ModuleInstance, Store, types};
3+
use tinywasm::{types, Extern, FuncContext, Imports, ModuleInstance, Store};
44
use types::{archive::AlignedVec, TinyWasmModule};
55

66
const WASM: &[u8] = include_bytes!("../../../examples/rust/out/tinywasm.opt.wasm");

crates/tinywasm/src/imports.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ impl From<&Import> for ExternName {
208208
/// imports
209209
/// .define("my_module", "print_i32", print_i32)?
210210
/// .define("my_module", "table", Extern::table(table_type, table_init))?
211-
/// .define("my_module", "memory", Extern::memory(MemoryType::new_32(1, Some(2))))?
211+
/// .define("my_module", "memory", Extern::memory(MemoryType::new_32(1, Some(2), None)))?
212212
/// .define("my_module", "global_i32", Extern::global(WasmValue::I32(666), false))?
213213
/// .link_module("my_other_module", 0)?;
214214
/// # Ok(())
@@ -317,22 +317,20 @@ impl Imports {
317317
actual: &MemoryType,
318318
real_size: Option<usize>,
319319
) -> Result<()> {
320-
Self::compare_types(import, &expected.arch, &actual.arch)?;
320+
Self::compare_types(import, &expected.arch(), &actual.arch())?;
321321

322-
if actual.page_count_initial > expected.page_count_initial
323-
&& real_size.map_or(true, |size| actual.page_count_initial > size as u64)
322+
if actual.page_count_initial() > expected.page_count_initial()
323+
&& real_size.map_or(true, |size| actual.page_count_initial() > size as u64)
324324
{
325325
return Err(LinkingError::incompatible_import_type(import).into());
326326
}
327327

328-
if expected.page_count_max.is_none() && actual.page_count_max.is_some() {
328+
if expected.page_size() != actual.page_size() {
329329
return Err(LinkingError::incompatible_import_type(import).into());
330330
}
331331

332-
if let (Some(expected_max), Some(actual_max)) = (expected.page_count_max, actual.page_count_max) {
333-
if actual_max < expected_max {
334-
return Err(LinkingError::incompatible_import_type(import).into());
335-
}
332+
if expected.page_count_max() > actual.page_count_max() {
333+
return Err(LinkingError::incompatible_import_type(import).into());
336334
}
337335

338336
Ok(())

crates/tinywasm/src/store/memory.rs

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@ use tinywasm_types::{MemoryType, ModuleInstanceAddr};
44

55
use crate::{cold, log, Error, Result};
66

7-
const PAGE_SIZE: usize = 65536;
8-
const MAX_PAGES: usize = 65536;
9-
const MAX_SIZE: u64 = PAGE_SIZE as u64 * MAX_PAGES as u64;
10-
117
/// A WebAssembly Memory Instance
128
///
139
/// See <https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances>
@@ -21,13 +17,13 @@ pub(crate) struct MemoryInstance {
2117

2218
impl MemoryInstance {
2319
pub(crate) fn new(kind: MemoryType, owner: ModuleInstanceAddr) -> Self {
24-
assert!(kind.page_count_initial <= kind.page_count_max.unwrap_or(MAX_PAGES as u64));
25-
log::debug!("initializing memory with {} pages", kind.page_count_initial);
20+
assert!(kind.page_count_initial() <= kind.page_count_max());
21+
log::debug!("initializing memory with {} pages of {} bytes", kind.page_count_initial(), kind.page_size());
2622

2723
Self {
2824
kind,
29-
data: vec![0; PAGE_SIZE * kind.page_count_initial as usize],
30-
page_count: kind.page_count_initial as usize,
25+
data: vec![0; kind.initial_size() as usize],
26+
page_count: kind.page_count_initial() as usize,
3127
_owner: owner,
3228
}
3329
}
@@ -58,7 +54,7 @@ impl MemoryInstance {
5854
}
5955

6056
pub(crate) fn max_pages(&self) -> usize {
61-
self.kind.page_count_max.unwrap_or(MAX_PAGES as u64) as usize
57+
self.kind.page_count_max() as usize
6258
}
6359

6460
pub(crate) fn load(&self, addr: usize, len: usize) -> Result<&[u8]> {
@@ -133,12 +129,12 @@ impl MemoryInstance {
133129
let new_pages = current_pages as i64 + pages_delta as i64;
134130
debug_assert!(new_pages <= i32::MAX as i64, "page count should never be greater than i32::MAX");
135131

136-
if new_pages < 0 || new_pages > MAX_PAGES as i64 || new_pages as usize > self.max_pages() {
132+
if new_pages < 0 || new_pages as usize > self.max_pages() {
137133
return None;
138134
}
139135

140-
let new_size = new_pages as usize * PAGE_SIZE;
141-
if new_size as u64 > MAX_SIZE {
136+
let new_size = (new_pages as u64 * self.kind.page_size()) as usize;
137+
if new_size as u64 > self.kind.max_size() {
142138
return None;
143139
}
144140

@@ -190,7 +186,7 @@ mod memory_instance_tests {
190186
use tinywasm_types::MemoryArch;
191187

192188
fn create_test_memory() -> MemoryInstance {
193-
let kind = MemoryType { arch: MemoryArch::I32, page_count_initial: 1, page_count_max: Some(2) };
189+
let kind = MemoryType::new(MemoryArch::I32, 1, Some(2), None);
194190
let owner = ModuleInstanceAddr::default();
195191
MemoryInstance::new(kind, owner)
196192
}
@@ -249,7 +245,7 @@ mod memory_instance_tests {
249245
#[test]
250246
fn test_memory_grow_out_of_bounds() {
251247
let mut memory = create_test_memory();
252-
assert!(memory.grow(MAX_PAGES as i32 + 1).is_none());
248+
assert!(memory.grow(memory.kind.max_size() as i32 + 1).is_none());
253249
}
254250

255251
#[test]
@@ -258,4 +254,29 @@ mod memory_instance_tests {
258254
assert_eq!(memory.grow(1), Some(1));
259255
assert_eq!(memory.grow(1), None);
260256
}
257+
258+
#[test]
259+
fn test_memory_custom_page_size_out_of_bounds() {
260+
let kind = MemoryType::new(MemoryArch::I32, 1, Some(2), Some(1));
261+
let owner = ModuleInstanceAddr::default();
262+
let mut memory = MemoryInstance::new(kind, owner);
263+
264+
let data_to_store = [1, 2];
265+
assert!(memory.store(0, data_to_store.len(), &data_to_store).is_err());
266+
}
267+
268+
#[test]
269+
fn test_memory_custom_page_size_grow() {
270+
let kind = MemoryType::new(MemoryArch::I32, 1, Some(2), Some(1));
271+
let owner = ModuleInstanceAddr::default();
272+
let mut memory = MemoryInstance::new(kind, owner);
273+
274+
assert_eq!(memory.grow(1), Some(1));
275+
276+
let data_to_store = [1, 2];
277+
assert!(memory.store(0, data_to_store.len(), &data_to_store).is_ok());
278+
279+
let loaded_data = memory.load(0, data_to_store.len()).unwrap();
280+
assert_eq!(loaded_data, &data_to_store);
281+
}
261282
}

0 commit comments

Comments
 (0)