diff --git a/README.md b/README.md index 28217e047..f2be2efe2 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Wabt has been compiled to JavaScript via emscripten. Some of the functionality i | [multi-memory][] | `--enable-multi-memory` | | ✓ | ✓ | ✓ | ✓ | ✓ | | [extended-const][] | `--enable-extended-const` | | ✓ | ✓ | ✓ | ✓ | ✓ | | [relaxed-simd][] | `--enable-relaxed-simd` | | ✓ | ✓ | ✓ | ✓ | | -| [custom-page-sizes][] | `--enable-custom-page-sizes`| | ✓ | ✓ | ✓ | ✓ | | +| [custom-page-sizes][] | `--enable-custom-page-sizes`| | ✓ | ✓ | ✓ | ✓ | ✓ | [exception handling]: https://github.com/WebAssembly/exception-handling [mutable globals]: https://github.com/WebAssembly/mutable-global diff --git a/src/c-writer.cc b/src/c-writer.cc index e7bcff4f8..e9b7fe96b 100644 --- a/src/c-writer.cc +++ b/src/c-writer.cc @@ -2229,18 +2229,17 @@ void CWriter::WriteDataInitializers() { Index memory_idx = module_->num_memory_imports; for (Index i = memory_idx; i < module_->memories.size(); i++) { const Memory* memory = module_->memories[i]; - uint64_t max; - if (memory->page_limits.has_max) { - max = memory->page_limits.max; - } else { - max = memory->page_limits.is_64 ? (static_cast(1) << 48) - : 65536; - } + const uint64_t max = + memory->page_limits.has_max + ? memory->page_limits.max + : WABT_BYTES_TO_MIN_PAGES( + (memory->page_limits.is_64 ? UINT64_MAX : UINT32_MAX), + memory->page_size); std::string func = GetMemoryAPIString(*memory, "wasm_rt_allocate_memory"); - Write(func, "(", - ExternalInstancePtr(ModuleFieldType::Memory, memory->name), ", ", - memory->page_limits.initial, ", ", max, ", ", - memory->page_limits.is_64, ");", Newline()); + Write( + func, "(", ExternalInstancePtr(ModuleFieldType::Memory, memory->name), + ", ", memory->page_limits.initial, ", ", max, ", ", + memory->page_limits.is_64, ", ", memory->page_size, ");", Newline()); } } @@ -2881,6 +2880,8 @@ void CWriter::WriteImportProperties(CWriterPhase kind) { write_import_prop(import, "max", "u64", limits->has_max ? limits->max : default_max); write_import_prop(import, "is64", "u8", limits->is_64); + write_import_prop(import, "pagesize", "u32", + cast(import)->memory.page_size); } else if (import->kind() == ExternalKind::Table) { const Limits* limits = &(cast(import)->table.elem_limits); const uint64_t default_max = std::numeric_limits::max(); diff --git a/src/tools/wasm2c.cc b/src/tools/wasm2c.cc index 68bc7a6ba..ea44be940 100644 --- a/src/tools/wasm2c.cc +++ b/src/tools/wasm2c.cc @@ -58,9 +58,12 @@ static const char s_description[] = )"; static const std::string supported_features[] = { - "multi-memory", "multi-value", "sign-extension", "saturating-float-to-int", - "exceptions", "memory64", "extended-const", "simd", - "threads", "tail-call"}; + "multi-memory", "multi-value", + "sign-extension", "saturating-float-to-int", + "exceptions", "memory64", + "extended-const", "simd", + "threads", "tail-call", + "custom-page-sizes"}; static bool IsFeatureSupported(const std::string& feature) { return std::find(std::begin(supported_features), std::end(supported_features), diff --git a/test/run-spec-wasm2c.py b/test/run-spec-wasm2c.py index 8dfa416ed..b0b0e070e 100755 --- a/test/run-spec-wasm2c.py +++ b/test/run-spec-wasm2c.py @@ -545,6 +545,7 @@ def main(args): parser.add_argument('--enable-extended-const', action='store_true') parser.add_argument('--enable-threads', action='store_true') parser.add_argument('--enable-tail-call', action='store_true') + parser.add_argument('--enable-custom-page-sizes', action='store_true') parser.add_argument('--disable-bulk-memory', action='store_true') parser.add_argument('--disable-reference-types', action='store_true') parser.add_argument('--debug-names', action='store_true') @@ -566,6 +567,7 @@ def main(args): '--enable-extended-const': options.enable_extended_const, '--enable-threads': options.enable_threads, '--enable-tail-call': options.enable_tail_call, + '--enable-custom-page-sizes': options.enable_custom_page_sizes, '--enable-multi-memory': options.enable_multi_memory, '--disable-bulk-memory': options.disable_bulk_memory, '--disable-reference-types': options.disable_reference_types, @@ -585,6 +587,7 @@ def main(args): '--enable-extended-const': options.enable_extended_const, '--enable-threads': options.enable_threads, '--enable-tail-call': options.enable_tail_call, + '--enable-custom-page-sizes': options.enable_custom_page_sizes, '--enable-multi-memory': options.enable_multi_memory}) options.cflags += shlex.split(os.environ.get('WASM2C_CFLAGS', '')) diff --git a/test/spec-wasm2c-prefix.c b/test/spec-wasm2c-prefix.c index c6cfecfa2..f470203de 100644 --- a/test/spec-wasm2c-prefix.c +++ b/test/spec-wasm2c-prefix.c @@ -403,7 +403,8 @@ static void init_spectest_module(w2c_spectest* instance) { instance->spectest_global_i64 = 666l; instance->spectest_global_f32 = 666.6; instance->spectest_global_f64 = 666.6; - wasm_rt_allocate_memory(&instance->spectest_memory, 1, 2, false); + wasm_rt_allocate_memory(&instance->spectest_memory, 1, 2, false, + WASM_DEFAULT_PAGE_SIZE); wasm_rt_allocate_funcref_table(&instance->spectest_table, 10, 20); wasm_rt_allocate_funcref_table(&instance->spectest_table64, 10, 20); } diff --git a/test/wasm2c/check-imports.txt b/test/wasm2c/check-imports.txt index 468bcfdde..c22ce7177 100644 --- a/test/wasm2c/check-imports.txt +++ b/test/wasm2c/check-imports.txt @@ -63,6 +63,7 @@ extern const u32 wasm2c_test_max_env_0x5F_indirect_function_table; extern const u64 wasm2c_test_min_env_0x5F_linear_memory; extern const u64 wasm2c_test_max_env_0x5F_linear_memory; extern const u8 wasm2c_test_is64_env_0x5F_linear_memory; +extern const u32 wasm2c_test_pagesize_env_0x5F_linear_memory; #ifdef __cplusplus } @@ -857,6 +858,7 @@ const u32 wasm2c_test_max_env_0x5F_indirect_function_table = 4294967295; const u64 wasm2c_test_min_env_0x5F_linear_memory = 0; const u64 wasm2c_test_max_env_0x5F_linear_memory = 65536; const u8 wasm2c_test_is64_env_0x5F_linear_memory = 0; +const u32 wasm2c_test_pagesize_env_0x5F_linear_memory = 65536; void wasm2c_test_instantiate(w2c_test* instance, struct w2c_env* w2c_env_instance) { assert(wasm_rt_is_initialized()); diff --git a/test/wasm2c/export-names.txt b/test/wasm2c/export-names.txt index 6e7a25b1b..dedf4c56c 100644 --- a/test/wasm2c/export-names.txt +++ b/test/wasm2c/export-names.txt @@ -48,6 +48,7 @@ wasm_rt_func_type_t wasm2c_test_get_func_type(uint32_t param_count, uint32_t res extern const u64 wasm2c_test_min_0x5Cmodule_import0x200x2A0x2F; extern const u64 wasm2c_test_max_0x5Cmodule_import0x200x2A0x2F; extern const u8 wasm2c_test_is64_0x5Cmodule_import0x200x2A0x2F; +extern const u32 wasm2c_test_pagesize_0x5Cmodule_import0x200x2A0x2F; /* export: '' */ void w2c_test_(w2c_test*); @@ -905,6 +906,7 @@ static void init_instance_import(w2c_test* instance, struct w2c_0x5Cmodule* w2c_ const u64 wasm2c_test_min_0x5Cmodule_import0x200x2A0x2F = 0; const u64 wasm2c_test_max_0x5Cmodule_import0x200x2A0x2F = 65536; const u8 wasm2c_test_is64_0x5Cmodule_import0x200x2A0x2F = 0; +const u32 wasm2c_test_pagesize_0x5Cmodule_import0x200x2A0x2F = 65536; void wasm2c_test_instantiate(w2c_test* instance, struct w2c_0x5Cmodule* w2c_0x5Cmodule_instance) { assert(wasm_rt_is_initialized()); diff --git a/test/wasm2c/hello.txt b/test/wasm2c/hello.txt index e89c6150b..84e9ec125 100644 --- a/test/wasm2c/hello.txt +++ b/test/wasm2c/hello.txt @@ -845,7 +845,7 @@ static const u8 data_segment_data_w2c_test_d0[] = { }; static void init_memories(w2c_test* instance) { - wasm_rt_allocate_memory(&instance->w2c_memory, 1, 65536, 0); + wasm_rt_allocate_memory(&instance->w2c_memory, 1, 65536, 0, 65536); LOAD_DATA(instance->w2c_memory, 8u, data_segment_data_w2c_test_d0, 14); } diff --git a/test/wasm2c/spec/custom-page-sizes/custom-page-sizes-invalid.txt b/test/wasm2c/spec/custom-page-sizes/custom-page-sizes-invalid.txt new file mode 100644 index 000000000..be553abbd --- /dev/null +++ b/test/wasm2c/spec/custom-page-sizes/custom-page-sizes-invalid.txt @@ -0,0 +1,6 @@ +;;; TOOL: run-spec-wasm2c +;;; ARGS*: --enable-custom-page-sizes --enable-multi-memory +;;; STDIN_FILE: third_party/testsuite/proposals/custom-page-sizes/custom-page-sizes-invalid.wast +(;; STDOUT ;;; +0/0 tests passed. +;;; STDOUT ;;) diff --git a/test/wasm2c/spec/custom-page-sizes/custom-page-sizes.txt b/test/wasm2c/spec/custom-page-sizes/custom-page-sizes.txt new file mode 100644 index 000000000..7563ad9e6 --- /dev/null +++ b/test/wasm2c/spec/custom-page-sizes/custom-page-sizes.txt @@ -0,0 +1,6 @@ +;;; TOOL: run-spec-wasm2c +;;; ARGS*: --enable-custom-page-sizes --enable-multi-memory +;;; STDIN_FILE: third_party/testsuite/proposals/custom-page-sizes/custom-page-sizes.wast +(;; STDOUT ;;; +24/24 tests passed. +;;; STDOUT ;;) diff --git a/wasm2c/README.md b/wasm2c/README.md index f032b4e2f..b7ab3bca2 100644 --- a/wasm2c/README.md +++ b/wasm2c/README.md @@ -297,13 +297,16 @@ typedef struct { Next is the definition of a memory instance. The `data` field is a pointer to `size` bytes of linear memory. The `size` field of `wasm_rt_memory_t` is the -current size of the memory instance in bytes, whereas `pages` is the current -size in pages (65536 bytes.) `max_pages` is the maximum number of pages as -specified by the module, or `0xffffffff` if there is no limit. +current size of the memory instance in bytes, `pages` is the current +size in pages, and `page_size` contains the page size in bytes (65,536 by default). +`max_pages` is the maximum number of pages specified by the module or allowed +by the memory index type (`is64` is true for memories that can grow to 2^64 bytes; +`false` for memories limited to 2^32 bytes). ```c typedef struct { uint8_t* data; + uint32_t page_size; uint64_t pages, max_pages; uint64_t size; bool is64; @@ -353,10 +356,10 @@ bool wasm_rt_is_initialized(void); void wasm_rt_free(void); void wasm_rt_trap(wasm_rt_trap_t) __attribute__((noreturn)); const char* wasm_rt_strerror(wasm_rt_trap_t trap); -void wasm_rt_allocate_memory(wasm_rt_memory_t*, uint32_t initial_pages, uint32_t max_pages, bool is64); +void wasm_rt_allocate_memory(wasm_rt_memory_t*, uint32_t initial_pages, uint32_t max_pages, bool is64, uint32_t page_size); uint32_t wasm_rt_grow_memory(wasm_rt_memory_t*, uint32_t pages); void wasm_rt_free_memory(wasm_rt_memory_t*); -void wasm_rt_allocate_memory_shared(wasm_rt_shared_memory_t*, uint32_t initial_pages, uint32_t max_pages, bool is64); +void wasm_rt_allocate_memory_shared(wasm_rt_shared_memory_t*, uint32_t initial_pages, uint32_t max_pages, bool is64, uint32_t page_size); uint32_t wasm_rt_grow_memory_shared(wasm_rt_shared_memory_t*, uint32_t pages); void wasm_rt_free_memory_shared(wasm_rt_shared_memory_t*); void wasm_rt_allocate_funcref_table(wasm_rt_table_t*, uint32_t elements, uint32_t max_elements); @@ -381,10 +384,12 @@ with `WASM_RT_TRAP_HANDLER` defined to the name of a trap handler function. The handler function should be a function taking a `wasm_rt_trap_t` as a parameter and returning `void`. e.g. `-DWASM_RT_TRAP_HANDLER=my_trap_handler` -`wasm_rt_allocate_memory` initializes a memory instance, and allocates at least -enough space for the given number of initial pages. The memory must be cleared -to zero. The `is64` parameter indicates if the memory is indexed with -an i32 or i64 address. +`wasm_rt_allocate_memory` initializes a memory instance, and allocates +at least enough space for the given number of initial pages, each of +size `page_size` (which must be `WASM_DEFAULT_PAGE_SIZE`, equal to 64 +KiB, unless using the custom-page-sizes feature). The memory must be +cleared to zero. The `is64` parameter indicates if the memory is +indexed with an i32 or i64 address. `wasm_rt_grow_memory` must grow the given memory instance by the given number of pages. If there isn't enough memory to do so, or the new page count would be @@ -399,7 +404,7 @@ arguments and returning `void` . e.g. `wasm_rt_free_memory` frees the memory instance. `wasm_rt_allocate_memory_shared` initializes a memory instance that can be -shared by different Wasm threads. It's operation is otherwise similar to +shared by different Wasm threads. Its operation is otherwise similar to `wasm_rt_allocate_memory`. `wasm_rt_grow_memory_shared` must grow the given shared memory instance by the @@ -658,8 +663,8 @@ int main(int argc, char** argv) { /* Create two `host` instances to store the memory and current string */ w2c_host host_1, host_2; - wasm_rt_allocate_memory(&host_1.memory, 1, 1, false); - wasm_rt_allocate_memory(&host_2.memory, 1, 1, false); + wasm_rt_allocate_memory(&host_1.memory, 1, 1, false, WASM_DEFAULT_PAGE_SIZE); + wasm_rt_allocate_memory(&host_2.memory, 1, 1, false, WASM_DEFAULT_PAGE_SIZE); /* Construct the `rot13` module instances */ w2c_rot13 rot13_1, rot13_2; diff --git a/wasm2c/examples/rot13/main.c b/wasm2c/examples/rot13/main.c index f0164fba8..bff10482e 100644 --- a/wasm2c/examples/rot13/main.c +++ b/wasm2c/examples/rot13/main.c @@ -42,7 +42,7 @@ int main(int argc, char** argv) { /* Create a structure to store the memory and current string, allocating 1 page of Wasm memory (64 KiB) that the rot13 module instance will import. */ struct w2c_host host; - wasm_rt_allocate_memory(&host.memory, 1, 1, false); + wasm_rt_allocate_memory(&host.memory, 1, 1, false, WASM_DEFAULT_PAGE_SIZE); // Construct an instance of the `rot13` module, which imports from the host. w2c_rot13 rot13; diff --git a/wasm2c/wasm-rt-mem-impl-helper.inc b/wasm2c/wasm-rt-mem-impl-helper.inc index 6d3e596c3..270fbff21 100644 --- a/wasm2c/wasm-rt-mem-impl-helper.inc +++ b/wasm2c/wasm-rt-mem-impl-helper.inc @@ -63,21 +63,28 @@ #endif +bool MEMORY_API_NAME(wasm_rt_memory_is_default32)(const MEMORY_TYPE* memory) { + return memory->page_size == WASM_DEFAULT_PAGE_SIZE && !memory->is64; +} + void MEMORY_API_NAME(wasm_rt_allocate_memory)(MEMORY_TYPE* memory, uint64_t initial_pages, uint64_t max_pages, - bool is64) { - uint64_t byte_length = initial_pages * WASM_PAGE_SIZE; + bool is64, + uint32_t page_size) { + uint64_t byte_length = initial_pages * page_size; memory->size = byte_length; memory->pages = initial_pages; memory->max_pages = max_pages; memory->is64 = is64; + memory->page_size = page_size; MEMORY_LOCK_VAR_INIT(memory->mem_lock); - if (WASM_RT_USE_MMAP && !is64) { + if (WASM_RT_USE_MMAP && + MEMORY_API_NAME(wasm_rt_memory_is_default32)(memory)) { #if WASM_RT_USE_MMAP // mmap-related functions don't exist unless this is set const uint64_t mmap_size = - get_alloc_size_for_mmap(memory->max_pages, memory->is64); + get_alloc_size_for_mmap_default32(memory->max_pages); void* addr = os_mmap(mmap_size); if (!addr) { os_print_last_error("os_mmap failed."); @@ -105,11 +112,12 @@ static uint64_t MEMORY_API_NAME(grow_memory_impl)(MEMORY_TYPE* memory, if (new_pages < old_pages || new_pages > memory->max_pages) { return (uint64_t)-1; } - uint64_t old_size = old_pages * WASM_PAGE_SIZE; - uint64_t new_size = new_pages * WASM_PAGE_SIZE; - uint64_t delta_size = delta * WASM_PAGE_SIZE; + uint64_t old_size = old_pages * memory->page_size; + uint64_t new_size = new_pages * memory->page_size; + uint64_t delta_size = delta * memory->page_size; MEMORY_CELL_TYPE new_data; - if (WASM_RT_USE_MMAP && !memory->is64) { + if (WASM_RT_USE_MMAP && + MEMORY_API_NAME(wasm_rt_memory_is_default32)(memory)) { #if WASM_RT_USE_MMAP new_data = memory->data; int ret = os_mprotect((void*)(new_data + old_size), delta_size); @@ -150,10 +158,11 @@ uint64_t MEMORY_API_NAME(wasm_rt_grow_memory)(MEMORY_TYPE* memory, } void MEMORY_API_NAME(wasm_rt_free_memory)(MEMORY_TYPE* memory) { - if (WASM_RT_USE_MMAP && !memory->is64) { + if (WASM_RT_USE_MMAP && + MEMORY_API_NAME(wasm_rt_memory_is_default32)(memory)) { #if WASM_RT_USE_MMAP const uint64_t mmap_size = - get_alloc_size_for_mmap(memory->max_pages, memory->is64); + get_alloc_size_for_mmap_default32(memory->max_pages); os_munmap((void*)memory->data, mmap_size); // ignore error #endif } else { diff --git a/wasm2c/wasm-rt-mem-impl.c b/wasm2c/wasm-rt-mem-impl.c index 0f22b25b6..5d933beb8 100644 --- a/wasm2c/wasm-rt-mem-impl.c +++ b/wasm2c/wasm-rt-mem-impl.c @@ -25,8 +25,6 @@ #include #endif -#define WASM_PAGE_SIZE 65536 - #ifdef WASM_RT_GROW_FAILED_HANDLER extern void WASM_RT_GROW_FAILED_HANDLER(); #endif @@ -112,7 +110,7 @@ static void os_print_last_error(const char* msg) { } } -#else +#else /* !_WIN32 */ static void* os_mmap(size_t size) { int map_prot = PROT_NONE; int map_flags = MAP_ANONYMOUS | MAP_PRIVATE; @@ -134,17 +132,16 @@ static void os_print_last_error(const char* msg) { perror(msg); } -#endif +#endif /* _WIN32 */ -static uint64_t get_alloc_size_for_mmap(uint64_t max_pages, bool is64) { - assert(!is64 && "memory64 is not yet compatible with WASM_RT_USE_MMAP"); +static uint64_t get_alloc_size_for_mmap_default32(uint64_t max_pages) { #if WASM_RT_MEMCHECK_GUARD_PAGES /* Reserve 8GiB. */ const uint64_t max_size = 0x200000000ul; return max_size; #else if (max_pages != 0) { - const uint64_t max_size = max_pages * WASM_PAGE_SIZE; + const uint64_t max_size = max_pages * WASM_DEFAULT_PAGE_SIZE; return max_size; } @@ -154,7 +151,7 @@ static uint64_t get_alloc_size_for_mmap(uint64_t max_pages, bool is64) { #endif } -#endif +#endif /* WASM_RT_USE_MMAP */ // Include operations for memory #define WASM_RT_MEM_OPS @@ -175,4 +172,3 @@ static uint64_t get_alloc_size_for_mmap(uint64_t max_pages, bool is64) { #undef WIN_MEMORY_LOCK_VAR_INIT #undef WIN_MEMORY_LOCK_AQUIRE #undef WIN_MEMORY_LOCK_RELEASE -#undef WASM_PAGE_SIZE diff --git a/wasm2c/wasm-rt.h b/wasm2c/wasm-rt.h index c135fdcd6..c289432b6 100644 --- a/wasm2c/wasm-rt.h +++ b/wasm2c/wasm-rt.h @@ -466,12 +466,12 @@ typedef void* wasm_rt_externref_t; typedef struct { /** The linear memory data, with a byte length of `size`. */ uint8_t* data; + /** The page size for this Memory object + (always 64 KiB without the custom-page-sizes feature) */ + uint32_t page_size; /** The current page count for this Memory object. */ uint64_t pages; - /** - * The maximum page count for this Memory object. If there is no maximum, - * `max_pages` is 0xffffffffu (i.e. UINT32_MAX). - */ + /** The maximum page count for this Memory object. */ uint64_t max_pages; /** The current size of the linear memory, in bytes. */ uint64_t size; @@ -494,12 +494,12 @@ typedef struct { * volatile. */ _Atomic volatile uint8_t* data; + /** The page size for this Memory object + (always 64 KiB without the custom-page-sizes feature) */ + uint32_t page_size; /** The current page count for this Memory object. */ uint64_t pages; - /** - * The maximum page count for this Memory object. If there is no maximum, - * `max_pages` is 0xffffffffu (i.e. UINT32_MAX). - */ + /* The maximum page count for this Memory object. */ uint64_t max_pages; /** The current size of the linear memory, in bytes. */ uint64_t size; @@ -601,6 +601,9 @@ const char* wasm_rt_strerror(wasm_rt_trap_t trap); #define wasm_rt_try(target) WASM_RT_SETJMP(target) +/** WebAssembly's default page size (64 KiB) */ +#define WASM_DEFAULT_PAGE_SIZE 65536 + /** * Initialize a Memory object with an initial page size of `initial_pages` and * a maximum page size of `max_pages`, indexed with an i32 or i64. @@ -609,13 +612,14 @@ const char* wasm_rt_strerror(wasm_rt_trap_t trap); * wasm_rt_memory_t my_memory; * // 1 initial page (65536 bytes), and a maximum of 2 pages, * // indexed with an i32 - * wasm_rt_allocate_memory(&my_memory, 1, 2, false); + * wasm_rt_allocate_memory(&my_memory, 1, 2, false, WASM_DEFAULT_PAGE_SIZE); * ``` */ void wasm_rt_allocate_memory(wasm_rt_memory_t*, uint64_t initial_pages, uint64_t max_pages, - bool is64); + bool is64, + uint32_t page_size); /** * Grow a Memory object by `pages`, and return the previous page count. If @@ -642,7 +646,8 @@ void wasm_rt_free_memory(wasm_rt_memory_t*); void wasm_rt_allocate_memory_shared(wasm_rt_shared_memory_t*, uint64_t initial_pages, uint64_t max_pages, - bool is64); + bool is64, + uint32_t page_size); /** Shared memory version of wasm_rt_grow_memory */ uint64_t wasm_rt_grow_memory_shared(wasm_rt_shared_memory_t*, uint64_t pages);