diff --git a/.gitignore b/.gitignore index b88109b38e0..1dd19478c2c 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ options-pinned.h # files related to building in-tree CMakeFiles *.cmake +!src/tools/wasm2c/gen-wasm2c-templates.cmake /CMakeCache.txt /Makefile /*.ninja diff --git a/.gitmodules b/.gitmodules index a674125e389..6251faea65b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,9 @@ [submodule "third_party/mimalloc"] path = third_party/mimalloc url = https://github.com/microsoft/mimalloc.git +[submodule "third_party/simde"] + path = third_party/simde + url = https://github.com/simd-everywhere/simde +[submodule "third_party/picosha2"] + path = third_party/picosha2 + url = https://github.com/okdshin/PicoSHA2 diff --git a/check.py b/check.py index d1e1f49103a..3f9897a87c8 100755 --- a/check.py +++ b/check.py @@ -25,7 +25,7 @@ from multiprocessing.pool import ThreadPool from pathlib import Path -from scripts.test import binaryenjs, finalize, shared, support, wasm2js, wasm_opt +from scripts.test import binaryenjs, finalize, shared, support, wasm2js, wasm2c, wasm_opt from scripts.test.shared import print_heading assert sys.version_info >= (3, 10), 'requires Python 3.10' @@ -441,6 +441,7 @@ def wrapper(*args, **kwargs): 'spec': run_spec_tests, 'finalize': finalize.test_wasm_emscripten_finalize, 'wasm2js': wasm2js.test_wasm2js, + 'wasm2c-spec': wasm2c.test_wasm2c_spec, 'validator': run_validator_tests, 'example': run_example_tests, 'unit': run_unittest, diff --git a/scripts/test/shared.py b/scripts/test/shared.py index d8211d8143a..7eec347ca36 100644 --- a/scripts/test/shared.py +++ b/scripts/test/shared.py @@ -213,6 +213,7 @@ def is_exe(fpath): WASM_AS = [os.path.join(options.binaryen_bin, 'wasm-as')] WASM_DIS = [os.path.join(options.binaryen_bin, 'wasm-dis')] WASM2JS = [os.path.join(options.binaryen_bin, 'wasm2js')] +WASM2C = [os.path.join(options.binaryen_bin, 'wasm2c')] WASM_CTOR_EVAL = [os.path.join(options.binaryen_bin, 'wasm-ctor-eval')] WASM_SHELL = [os.path.join(options.binaryen_bin, 'wasm-shell')] WASM_REDUCE = [os.path.join(options.binaryen_bin, 'wasm-reduce')] diff --git a/scripts/test/wasm2c.py b/scripts/test/wasm2c.py new file mode 100644 index 00000000000..52ee502cac9 --- /dev/null +++ b/scripts/test/wasm2c.py @@ -0,0 +1,67 @@ +# Copyright 2026 WebAssembly Community Group participants +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pathlib +import subprocess +import tempfile + +from . import shared, support +from .shared import print_heading + +spec_tests = [ +] + +def test_wasm2c_spec_output(): + for t in shared.options.spec_tests: + test_path = pathlib.Path(t) + if test_path.name not in spec_tests: + continue + + print('..', test_path.name) + + is_fail_test = '.fail' in test_path.name + + test_subdir = f'wasm2c_spec_{test_path.stem}' + test_subdir_path = pathlib.Path(test_subdir) + test_subdir_path.mkdir(exist_ok=True) + + wasm2c_cmd = [shared.WASM2C[0], t, '-o', f'{test_subdir}/{test_path.stem}.c', '--allow-asserts'] + support.run_command(wasm2c_cmd, expected_status = (1 if is_fail_test else 0)) + + c_sources = sorted(test_subdir_path.glob('*.c')) + + wasm_rt_dir = pathlib.Path(shared.options.binaryen_root) / 'src' / 'tools' / 'wasm2c' / 'wasm-rt' + c_sources.append(wasm_rt_dir / 'wasm-rt-impl.c') + c_sources.append(wasm_rt_dir / 'wasm-rt-mem-impl.c') + c_sources.append(wasm_rt_dir / 'wasm-rt-exceptions-impl.c') + + compile_cmd = [shared.NATIVECC, '-O2', '-std=c11', '-D_GNU_SOURCE', '-D_DEFAULT_SOURCE', '-I.', f"-I{wasm_rt_dir}"] + [str(s) for s in c_sources] + ['-o', f'{test_subdir}/spec_test_runner'] + + compile_cmd += ['-fno-optimize-sibling-calls', '-frounding-math'] + if 'gcc' in shared.NATIVECC.lower(): + compile_cmd.append('-fsignaling-nans') + + compile_cmd.append('-lm') + compile_cmd.append('-lpthread') + + support.run_command(compile_cmd) + + # Run spec test runner binary and assert success + support.run_command([f'{test_subdir}/spec_test_runner']) + +def test_wasm2c_spec(): + print_heading('checking wasm2c spec testcases...') + if shared.skip_if_on_windows('wasm2c-spec'): + return + test_wasm2c_spec_output() diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index 91fea46b1df..4bd2c41d0b3 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -11,6 +11,7 @@ set(fuzzing_SOURCES binaryen_add_executable(wasm-opt "${fuzzing_SOURCES};wasm-opt.cpp") binaryen_add_executable(wasm-metadce wasm-metadce.cpp) binaryen_add_executable(wasm2js wasm2js.cpp) +add_subdirectory(wasm2c) binaryen_add_executable(wasm-emscripten-finalize wasm-emscripten-finalize.cpp) binaryen_add_executable(wasm-as wasm-as.cpp) binaryen_add_executable(wasm-dis wasm-dis.cpp) diff --git a/src/tools/wasm2c/CMakeLists.txt b/src/tools/wasm2c/CMakeLists.txt new file mode 100644 index 00000000000..dd37a168dd5 --- /dev/null +++ b/src/tools/wasm2c/CMakeLists.txt @@ -0,0 +1,43 @@ +set(TEMPLATE_CMAKE ${CMAKE_CURRENT_SOURCE_DIR}/gen-wasm2c-templates.cmake) +set(TEMPLATE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/templates) +set(PREBUILT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/prebuilt) + +add_custom_command( + OUTPUT ${PREBUILT_DIR}/wasm2c_header_top.cpp + ${PREBUILT_DIR}/wasm2c_header_bottom.cpp + ${PREBUILT_DIR}/wasm2c_source_includes.cpp + ${PREBUILT_DIR}/wasm2c_source_declarations.cpp + ${PREBUILT_DIR}/wasm2c_simd_source_declarations.cpp + ${PREBUILT_DIR}/wasm2c_atomicops_source_declarations.cpp + ${PREBUILT_DIR}/wasm2c_spec_top.cpp + + COMMAND ${CMAKE_COMMAND} -D out="${PREBUILT_DIR}/wasm2c_header_top.cpp" -D in="${TEMPLATE_DIR}/wasm2c.top.h" -D symbol="HeaderTop" -P ${TEMPLATE_CMAKE} + COMMAND ${CMAKE_COMMAND} -D out="${PREBUILT_DIR}/wasm2c_header_bottom.cpp" -D in="${TEMPLATE_DIR}/wasm2c.bottom.h" -D symbol="HeaderBottom" -P ${TEMPLATE_CMAKE} + COMMAND ${CMAKE_COMMAND} -D out="${PREBUILT_DIR}/wasm2c_source_includes.cpp" -D in="${TEMPLATE_DIR}/wasm2c.includes.c" -D symbol="SourceIncludes" -P ${TEMPLATE_CMAKE} + COMMAND ${CMAKE_COMMAND} -D out="${PREBUILT_DIR}/wasm2c_source_declarations.cpp" -D in="${TEMPLATE_DIR}/wasm2c.declarations.c" -D symbol="SourceDeclarations" -P ${TEMPLATE_CMAKE} + COMMAND ${CMAKE_COMMAND} -D out="${PREBUILT_DIR}/wasm2c_simd_source_declarations.cpp" -D in="${TEMPLATE_DIR}/wasm2c_simd.declarations.c" -D symbol="SimdSourceDeclarations" -P ${TEMPLATE_CMAKE} + COMMAND ${CMAKE_COMMAND} -D out="${PREBUILT_DIR}/wasm2c_atomicops_source_declarations.cpp" -D in="${TEMPLATE_DIR}/wasm2c_atomicops.declarations.c" -D symbol="AtomicsOpsSourceDeclarations" -P ${TEMPLATE_CMAKE} + COMMAND ${CMAKE_COMMAND} -D out="${PREBUILT_DIR}/wasm2c_spec_top.cpp" -D in="${TEMPLATE_DIR}/wasm2c_spec.top.c" -D symbol="SpecTop" -P ${TEMPLATE_CMAKE} + + DEPENDS ${TEMPLATE_DIR}/wasm2c.top.h + ${TEMPLATE_DIR}/wasm2c.bottom.h + ${TEMPLATE_DIR}/wasm2c.includes.c + ${TEMPLATE_DIR}/wasm2c.declarations.c + ${TEMPLATE_DIR}/wasm2c_simd.declarations.c + ${TEMPLATE_DIR}/wasm2c_atomicops.declarations.c + ${TEMPLATE_DIR}/wasm2c_spec.top.c +) + +set(WASM2C_GENERATED_SOURCES + ${PREBUILT_DIR}/wasm2c_header_top.cpp + ${PREBUILT_DIR}/wasm2c_header_bottom.cpp + ${PREBUILT_DIR}/wasm2c_source_includes.cpp + ${PREBUILT_DIR}/wasm2c_source_declarations.cpp + ${PREBUILT_DIR}/wasm2c_simd_source_declarations.cpp + ${PREBUILT_DIR}/wasm2c_atomicops_source_declarations.cpp + ${PREBUILT_DIR}/wasm2c_spec_top.cpp +) + +binaryen_add_executable(wasm2c "wasm2c-builder.cpp;assertion-emitter.cpp;wasm2c.cpp;${WASM2C_GENERATED_SOURCES}") +# TODO: https://github.com/WebAssembly/binaryen/issues/8775 - stop using picosha2 +target_include_directories(wasm2c PRIVATE ${PROJECT_SOURCE_DIR}/third_party/picosha2 ${PROJECT_SOURCE_DIR}/src/tools/wasm2c) diff --git a/src/tools/wasm2c/README.md b/src/tools/wasm2c/README.md new file mode 100644 index 00000000000..b7ab3bca2e4 --- /dev/null +++ b/src/tools/wasm2c/README.md @@ -0,0 +1,722 @@ +# wasm2c: Convert wasm files to C source and header + +`wasm2c` takes a WebAssembly module and produces an equivalent C source and +header. Some examples: + +```sh +# parse binary file test.wasm and write test.c and test.h +$ wasm2c test.wasm -o test.c + +# parse test.wasm, write test.c and test.h, but ignore the debug names, if any +$ wasm2c test.wasm --no-debug-names -o test.c +``` + +The C code produced targets the C99 standard. If, however, the Wasm module uses +Wasm threads/atomics, the code produced targets the C11 standard. + +## Tutorial: .wat -> .wasm -> .c + +Let's look at a simple example of a factorial function. + +```wasm +(memory $mem 1) +(func (export "fac") (param $x i32) (result i32) + (if (result i32) (i32.eq (local.get $x) (i32.const 0)) + (then (i32.const 1)) + (else + (i32.mul (local.get $x) (call 0 (i32.sub (local.get $x) (i32.const 1)))) + ) + ) +) +``` + +Save this to `fac.wat`. We can convert this to a `.wasm` file by using the +`wat2wasm` tool: + +```sh +$ wat2wasm fac.wat -o fac.wasm +``` + +We can then convert it to a C source and header by using the `wasm2c` tool: + +```sh +$ wasm2c fac.wasm -o fac.c +``` + +This generates two files, `fac.c` and `fac.h`. We'll take a closer look at +these files below, but first let's show a simple example of how to use these +files. + +## Using the generated module + +To actually use our `fac` module, we'll use create a new file, `main.c`, that +include `fac.h`, initializes the module, and calls `fac`. + +`wasm2c` generates a few C symbols based on the `fac.wasm` module. +The first is `w2c_fac`, a type that represents an instance of the +`fac` module. `wasm2c` generates functions that construct and free a +`w2c_fac` instance: `wasm2c_fac_instantiate` and +`wasm2c_fac_free`. Finally, `wasm2c` generates the exported `fac` +function itself (`w2c_fac_fac`), which acts on a `w2c_fac` instance. + +All the exported symbols shared a common module ID (`fac`) which, by default, is +based on the name section in the module or the name of input file. This prefix +can be overridden using the `-n/--module-name` command line flag. + +```c +#include +#include + +#include "fac.h" + +int main(int argc, char** argv) { + /* Make sure there is at least one command-line argument. */ + if (argc < 2) { + printf("Invalid argument. Expected '%s NUMBER'\n", argv[0]); + return 1; + } + + /* Convert the argument from a string to an int. We'll implicitly cast the int + to a `u32`, which is what `fac` expects. */ + u32 x = atoi(argv[1]); + + /* Initialize the Wasm runtime. */ + wasm_rt_init(); + + /* Declare an instance of the `fac` module. */ + w2c_fac fac; + + /* Construct the module instance. */ + wasm2c_fac_instantiate(&fac); + + /* Call `fac`, using the mangled name. */ + u32 result = w2c_fac_fac(&fac, x); + + /* Print the result. */ + printf("fac(%u) -> %u\n", x, result); + + /* Free the fac module. */ + wasm2c_fac_free(&fac); + + /* Free the Wasm runtime state. */ + wasm_rt_free(); + + return 0; +} + +``` + +## Compiling the wasm2c output + +To compile the executable, we need to use `main.c` and the generated `fac.c`. +We'll also include `wasm-rt-impl.c` and `wasm-rt-mem-impl.c`, which have implementations of the various +`wasm_rt_*` functions used by `fac.c` and `fac.h`. + +```sh +$ cc -o fac main.c fac.c wasm2c/wasm-rt-impl.c wasm2c/wasm-rt-mem-impl.c -Iwasm2c -lm +``` + +A note on compiling with optimization: wasm2c relies on certain +behavior from the C compiler to maintain conformance with the +WebAssembly specification, especially with regards to requirements to +convert "signaling" to "quiet" floating-point NaN values and for +infinite recursion to produce a trap. When compiling with optimization +(e.g. `-O2` or `-O3`), it's necessary to disable some optimizations to +preserve conformance. With GCC 11, adding the command-line arguments +`-fno-optimize-sibling-calls -frounding-math -fsignaling-nans` appears +to be sufficient. With clang 14, just `-fno-optimize-sibling-calls +-frounding-math` appears to be sufficient. + +Now let's test it out! + +```sh +$ ./fac 1 +fac(1) -> 1 +$ ./fac 5 +fac(5) -> 120 +$ ./fac 10 +fac(10) -> 3628800 +``` + +You can take a look at the all of these files in +[wasm2c/examples/fac](/wasm2c/examples/fac). + +### Enabling extra sanity checks + +Wasm2c provides a macro `WASM_RT_SANITY_CHECKS` that if defined enables +additional sanity checks in the produced Wasm2c code. Note that this may have a +high performance overhead, and is thus only recommended for debug builds. + +### Enabling Segue (a Linux x86_64 target specific optimization) + +Wasm2c can use the "Segue" optimization if allowed. The segue optimization uses +an x86 segment register to store the location of Wasm's linear memory, when +compiling a Wasm module with clang, running on x86_64 Linux, the macro +`WASM_RT_ALLOW_SEGUE` is defined, and the flag `-mfsgsbase` is passed to clang. +Segue is not used if + +1. The Wasm module uses a more than a single unshared imported or exported + memory +2. The wasm2c code is compiled with GCC. Segue requires intrinsics for + (rd|wr)gsbase, "address namespaces" for accessing pointers, and support for + memcpy on pointers with custom "address namespaces". GCC does not support the + memcpy requirement. +3. The code is compiled for Windows as Windows doesn't restore the segment + register on context switch. + +The wasm2c generated code automatically sets the unused segment register (the +`%gs` register on x86_64 Linux) during the function calls into wasm2c generated +module, restores it after calls to external modules etc. Any host function +written in C would continue to work without changes as C code does not modify +the unused segment register `%gs` (See +[here](https://www.kernel.org/doc/html/next/x86/x86_64/fsgs.html) for details). +However, any host functions written in assembly that clobber the free segment +register must restore the value of this register prior to executing or returning +control to wasm2c generated code. + +As an additional optimization, if the host program does not use the `%gs` +segment register for any other purpose (which is typically the case in most +programs), you can additionally allow wasm2c to unconditionally overwrite the +value of the `%gs` register without restoring the old value. This can be done +defining the macro `WASM_RT_SEGUE_FREE_SEGMENT`. + +You can test the performance of the Segue optimization by running Dhrystone with +and without Segue: + +```bash +cd wasm2c/benchmarks/segue && make +``` + +## Looking at the generated header, `fac.h` + +The generated header file looks something like this: + +```c +/* Automatically generated by wasm2c */ +#ifndef FAC_H_GENERATED_ +#define FAC_H_GENERATED_ + +... + +#include "wasm-rt.h" + +... +#ifndef WASM_RT_CORE_TYPES_DEFINED +#define WASM_RT_CORE_TYPES_DEFINED + +... + +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct w2c_fac { + char dummy_member; +} w2c_fac; + +void wasm2c_fac_instantiate(w2c_fac*); +void wasm2c_fac_free(w2c_fac*); +wasm_rt_func_type_t wasm2c_fac_get_func_type(uint32_t param_count, uint32_t result_count, ...); + +/* export: 'fac' */ +u32 w2c_fac_fac(w2c_fac*, u32); + +#ifdef __cplusplus +} +#endif + +#endif /* FAC_H_GENERATED_ */ + +``` + +Let's look at each section. The outer `#ifndef` is standard C +boilerplate for a header. This `WASM_RT_CORE_TYPES_DEFINED` section +contains a number of definitions required for all WebAssembly +modules. The `extern "C"` part makes sure to not mangle the symbols if +using this header in C++. + +The included `wasm-rt.h` file also includes a number of relevant definitions. +First is the `wasm_rt_trap_t` enum, which is used to give the reason a trap +occurred. + +```c +typedef enum { + WASM_RT_TRAP_NONE, + WASM_RT_TRAP_OOB, + WASM_RT_TRAP_INT_OVERFLOW, + WASM_RT_TRAP_DIV_BY_ZERO, + WASM_RT_TRAP_INVALID_CONVERSION, + WASM_RT_TRAP_UNREACHABLE, + WASM_RT_TRAP_CALL_INDIRECT, + WASM_RT_TRAP_UNCAUGHT_EXCEPTION, + WASM_RT_TRAP_EXHAUSTION, +} wasm_rt_trap_t; +``` + +Next is the `wasm_rt_type_t` enum, which is used for specifying function +signatures. Six WebAssembly value types are included: + +```c +typedef enum { + WASM_RT_I32, + WASM_RT_I64, + WASM_RT_F32, + WASM_RT_F64, + WASM_RT_FUNCREF, + WASM_RT_EXTERNREF, +} wasm_rt_type_t; + +Next is `wasm_rt_function_ptr_t`, the function signature for a generic function +callback. Since a WebAssembly table can contain functions of any given +signature, it is necessary to convert them to a canonical form: + +```c +typedef void (*wasm_rt_function_ptr_t)(void); +``` + +Next is the definition for a function reference (in WebAssembly 1.0, +this was the type of all table elements, but funcrefs can now also be +used as ordinary values, and tables can alternately be declared as +type externref). In this structure, `wasm_rt_func_type_t` is an opaque +256-bit ID that can be looked up via the `Z_[modname]_get_func_type` +function. (A demonstration of this can be found in the `callback` +example.) `module_instance` is the pointer to the function's +originating module instance, which will be passed in when the func is +called. + +```c +typedef struct { + wasm_rt_func_type_t func_type; + wasm_rt_function_ptr_t func; + void* module_instance; +} wasm_rt_funcref_t; + +``` + +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, `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; +} wasm_rt_memory_t; +``` + +This is followed by the definition of a shared memory instance. This is similar +to a regular memory instance, but represents memory that can be used by multiple +Wasm instances, and thus enforces a minimum amount of memory order on +operations. The Shared memory definition has one additional member, `mem_lock`, +which is a lock that is used during memory grow operations for thread safety. + +```c +typedef struct { + _Atomic volatile uint8_t* data; + uint64_t pages, max_pages; + uint64_t size; + bool is64; + mtx_t mem_lock; +} wasm_rt_shared_memory_t; +``` + +Next is the definition of a table instance. The `data` field is a pointer to +`size` elements. Like a memory instance, `size` is the current size of a table, +and `max_size` is the maximum size of the table, or `0xffffffff` if there is no +limit. + +```c +typedef struct { + wasm_rt_funcref_t* data; + uint32_t max_size; + uint32_t size; +} wasm_rt_funcref_table_t; +``` + +## Symbols that must be defined by the embedder + +Next in `wasm-rt.h` are a collection of function declarations that must be implemented by +the embedder (i.e. you) before this C source can be used. + +A C implementation of these functions is defined in +[`wasm-rt-impl.h`](wasm-rt-impl.h) and [`wasm-rt-impl.c`](wasm-rt-impl.c). + +```c +void wasm_rt_init(void); +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, 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, 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); +void wasm_rt_allocate_externref_table(wasm_rt_externref_table_t*, uint32_t elements, uint32_t max_elements); +void wasm_rt_free_funcref_table(wasm_rt_table_t*); +void wasm_rt_free_externref_table(wasm_rt_table_t*); +uint32_t wasm_rt_call_stack_depth; /* on platforms that don't use the signal handler to detect exhaustion */ +void wasm_rt_init_thread(void); +void wasm_rt_free_thread(void); +``` + +`wasm_rt_init` must be called by the embedder before anything else, to +initialize the runtime. `wasm_rt_free` frees any global +state. `wasm_rt_is_initialized` can be used to confirm that the +runtime has been initialized. + +`wasm_rt_trap` is a function that is called when the module traps. Some possible +implementations are to throw a C++ exception, or to just abort the program +execution. The default runtime included in wasm2c unwinds the stack using +`longjmp`. The host can overide this call to `longjmp` by compiling the runtime +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, 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 +greater than the maximum page count, the function must fail by returning +`0xffffffff`. If the function succeeds, it must return the previous size of the +memory instance, in pages. The host can optionally be notified of failures by +compiling the runtime with `WASM_RT_GROW_FAILED_HANDLER` defined to the name of +a handler function. The handler function should be a function taking no +arguments and returning `void` . e.g. +`-DWASM_RT_GROW_FAILED_HANDLER=my_growfail_handler` + +`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. Its operation is otherwise similar to +`wasm_rt_allocate_memory`. + +`wasm_rt_grow_memory_shared` must grow the given shared memory instance by the +given number of pages. It's operation is otherwise similar to +`wasm_rt_grow_memory`. + +`wasm_rt_free_memory_shared` frees the shared memory instance. + +`wasm_rt_allocate_funcref_table` and the similar `..._externref_table` +initialize a table instance of the given type, and allocate at least +enough space for the given number of initial elements. The elements +must be cleared to zero. + +`wasm_rt_free_funcref_table` and `..._externref_table` free the table instance. + +`wasm_rt_call_stack_depth` is the current stack call depth. Since this is +shared between modules, it must be defined only once, by the embedder. +It is only used on platforms that don't use the signal handler to detect +exhaustion. + +`wasm_rt_init_thread` and `wasm_rt_free_thread` are used to initialize +and free the runtime state for a given thread (other than the one that +called `wasm_rt_init`). An example can be found in +`wasm2c/examples/threads`. + +### Runtime support for exception handling + +Several additional symbols must be defined if wasm2c is being run with support +for exceptions (`--enable-exceptions`). These are defined in +`wasm-rt-exceptions.h`. These symbols are: + +```c +void wasm_rt_load_exception(const char* tag, uint32_t size, const void* values); +WASM_RT_NO_RETURN void wasm_rt_throw(void); +WASM_RT_UNWIND_TARGET +WASM_RT_UNWIND_TARGET* wasm_rt_get_unwind_target(void); +void wasm_rt_set_unwind_target(WASM_RT_UNWIND_TARGET* target); +uint32_t wasm_rt_exception_tag(void); +uint32_t wasm_rt_exception_size(void); +void* wasm_rt_exception(void); +wasm_rt_try(target) +``` + +A C implementation of these functions is also available in +[`wasm-rt-exceptions-impl.c`](wasm-rt-exceptions-impl.c). + +`wasm_rt_load_exception` sets the active exception to a given tag, size, and contents. + +`wasm_rt_throw` throws the active exception. + +`WASM_RT_UNWIND_TARGET` is the type of an unwind target if an +exception is thrown and caught. + +`wasm_rt_get_unwind_target` gets the current unwind target if an exception is thrown. + +`wasm_rt_set_unwind_target` sets the unwind target if an exception is thrown. + +Three functions provide access to the active exception: +`wasm_rt_exception_tag`, `wasm_rt_exception_size`, and +`wasm_rt_exception` return its tag, size, and contents, respectively. + +`wasm_rt_try(target)` is a macro that captures the current calling +environment as an unwind target and stores it into `target`, which +must be of type `WASM_RT_UNWIND_TARGET`. + +## Exported symbols + +Finally, `fac.h` defines the module instance type (which in the case +of `fac` is essentially empty), and the exported symbols provided by +the module. In our example, the only function we exported was +`fac`. + +`wasm2c_fac_instantiate(w2c_fac*)` creates an instance of +the module and must be called before the module instance can be +used. `wasm2c_fac_free(w2c_fac*)` frees the instance. +`wasm2c_fac_get_func_type` can be used to look up a function type ID +at runtime. It is a variadic function where the first two arguments +give the number of parameters and results, and the following arguments +are the types from the wasm_rt_type_t enum described above. The +`callback` example demonstrates using this to pass a host function to +a WebAssembly module dynamically at runtime. + +```c +typedef struct w2c_fac { + char dummy_member; +} w2c_fac; + +void wasm2c_fac_instantiate(w2c_fac*); +void wasm2c_fac_free(w2c_fac*); +wasm_rt_func_type_t wasm2c_fac_get_func_type(uint32_t param_count, uint32_t result_count, ...); + +/* export: 'fac' */ +u32 w2c_fac_fac(w2c_fac*, u32); +``` + +## Handling other kinds of imports and exports of modules + +Exported functions are handled by declaring a prefixed equivalent +function in the header. If a module imports a function, `wasm2c` +declares the function in the output header file, and the host function +is responsible for defining the function. + +Exports of other kinds (globals, memories, tables) are handled +differently, since they are part of the module instance, and each +instance can have its own exports. For these cases, `wasm2c` provides +a function that takes in a module instance as argument, and returns +the corresponding export. For example, if `fac` exported a memory as +such: + +```wasm +(export "mem" (memory $mem)) +``` + +then `wasm2c` would declare the following function in the header: + +```c +/* export: 'mem' */ +wasm_rt_memory_t* w2c_fac_mem(w2c_fac* instance); + +``` + +which would be defined as: +```c +/* export: 'mem' */ +wasm_rt_memory_t* w2c_fac_mem(w2c_fac* instance) { + return &instance->w2c_mem; +} +``` + +## A quick look at `fac.c` + +The contents of `fac.c` are internals, but it is useful to see a little about +how it works. + +The first few hundred lines define macros that are used to implement the +various WebAssembly instructions. Their implementations may be interesting to +the curious reader, but are out of scope for this document. + +Following those definitions are various initialization functions (`init`, `free`, +`init_func_types`, `init_globals`, `init_memory`, `init_table`, and +`init_exports`.) In our example, most of these functions are empty, since the +module doesn't use any globals, memory or tables. + +The most interesting part is the definition of the function `fac`: + +```c +static u32 w2c_fac_fac_0(w2c_fac* instance, u32 var_p0) { + FUNC_PROLOGUE; + u32 var_i0, var_i1, var_i2; + var_i0 = var_p0; + var_i1 = 0u; + var_i0 = var_i0 == var_i1; + if (var_i0) { + var_i0 = 1u; + } else { + var_i0 = var_p0; + var_i1 = var_p0; + var_i2 = 1u; + var_i1 -= var_i2; + var_i1 = w2c_fac_fac_0(instance, var_i1); + var_i0 *= var_i1; + } + FUNC_EPILOGUE; + return var_i0; +} +``` + +If you look at the original WebAssembly text in the flat format, you can see +that there is a 1-1 mapping in the output: + +```wasm +(func $fac (param $x i32) (result i32) + local.get $x + i32.const 0 + i32.eq + if (result i32) + i32.const 1 + else + local.get $x + local.get $x + i32.const 1 + i32.sub + call 0 + i32.mul + end) +``` + +This looks different than the factorial function above because it is using the +"flat format" instead of the "folded format". You can use `wat-desugar` to +convert between the two to be sure: + +```sh +$ wat-desugar fac-flat.wat --fold -o fac-folded.wat +``` + +```wasm +(module + (func (;0;) (param i32) (result i32) + (if (result i32) ;; label = @1 + (i32.eq + (local.get 0) + (i32.const 0)) + (then + (i32.const 1)) + (else + (i32.mul + (local.get 0) + (call 0 + (i32.sub + (local.get 0) + (i32.const 1))))))) + (export "fac" (func 0)) + (type (;0;) (func (param i32) (result i32)))) +``` + +The formatting is different and the variable and function names are gone, but +the structure is the same. + +## Create multiple instances of a module + +Since information about the execution context, such as memories, is encapsulated +in the module instance structure, and a pointer to the structure is being passed through +function calls, multiple instances of the same module can be instantiated alongside +one another. + +We can take a look at another version of the `main` function for a `rot13` example. By +declaring two sets of context information, two instances of `rot13` can be instantiated +in the same address space. + +```c +#include +#include +#include + +#include "rot13.h" + +/* Define structure to hold the imports */ +typedef struct w2c_host { + wasm_rt_memory_t memory; + char* input; +} w2c_host; + +/* Accessor to access the memory member of the host */ +wasm_rt_memory_t* w2c_host_mem(w2c_host* instance) { + return &instance->memory; +} + +int main(int argc, char** argv) { + /* Make sure there is at least one command-line argument. */ + if (argc < 2) { + printf("Invalid argument. Expected '%s WORD...'\n", argv[0]); + return 1; + } + /* Initialize the Wasm runtime. */ + wasm_rt_init(); + + /* 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_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; + wasm2c_rot13_instantiate(&rot13_1, &host_1); + wasm2c_rot13_instantiate(&rot13_2, &host_2); + + /* Call `rot13` on the first two arguments. */ + assert(argc > 2); + host_1.input = argv[1]; + w2c_rot13_rot13(&rot13_1); + host_2.input = argv[2]; + w2c_rot13_rot13(&rot13_2); + + /* Free the rot13 instances. */ + wasm2c_rot13_free(&rot13_1); + wasm2c_rot13_free(&rot13_2); + + /* Free the Wasm runtime state. */ + wasm_rt_free(); + + return 0; +} + +/* Fill the wasm buffer with the input to be rot13'd. + * + * params: + * instance: An instance of the w2c_host structure + * ptr: The wasm memory address of the buffer to fill data. + * size: The size of the buffer in wasm memory. + * result: + * The number of bytes filled into the buffer. (Must be <= size). + */ +u32 w2c_host_fill_buf(w2c_host* instance, u32 ptr, u32 size) { + for (size_t i = 0; i < size; ++i) { + if (instance->input[i] == 0) { + return i; + } + instance->memory.data[ptr + i] = instance->input[i]; + } + return size; +} + +/* Called when the wasm buffer has been rot13'd. + * + * params: + * w2c_host: An instance of the w2c_host structure + * ptr: The wasm memory address of the buffer. + * size: The size of the buffer in wasm memory. + */ +void w2c_host_buf_done(w2c_host* instance, u32 ptr, u32 size) { + /* The output buffer is not necessarily null-terminated, so use the %*.s + * printf format to limit the number of characters printed. */ + printf("%s -> %.*s\n", instance->input, (int)size, &instance->memory.data[ptr]); +} +``` diff --git a/src/tools/wasm2c/assertion-emitter.cpp b/src/tools/wasm2c/assertion-emitter.cpp new file mode 100644 index 00000000000..895a40fdb27 --- /dev/null +++ b/src/tools/wasm2c/assertion-emitter.cpp @@ -0,0 +1,117 @@ +/* + * Copyright 2026 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "parser/wat-parser.h" +#include "support/file.h" +#include "tools/wasm2c/assertion-emitter.h" +#include "tools/wasm2c/c-printer.h" +#include "wasm.h" + +// code to be inserted into the generated output +extern const char* SpecTop; + +namespace wasm { + +namespace { + +inline std::string stripExtension(const std::string& path) { + size_t lastDot = path.find_last_of('.'); + if (lastDot == std::string::npos) { + return path; + } + size_t lastSlash = path.find_last_of("/\\"); + if (lastSlash != std::string::npos && lastDot < lastSlash) { + return path; + } + return path.substr(0, lastDot); +} + +inline std::string getBasename(const std::string& path) { + size_t lastSlash = path.find_last_of("/\\"); + if (lastSlash == std::string::npos) { + return path; + } + return path.substr(lastSlash + 1); +} + +} // anonymous namespace + +AssertionEmitter::AssertionEmitter(WATParser::WASTScript& script, + Wasm2CBuilder::Flags flags) + : script(script), flags(flags) {} + +void AssertionEmitter::emit(std::ostream& cOut, + const std::string& outputCPath) { + CPrinter c(cOut); + + std::string basePath = + outputCPath.empty() ? "spec" : stripExtension(outputCPath); + std::string baseBasename = getBasename(basePath); + + // Loop sequentially through WASTScript AST commands + for (size_t i = 0; i < script.size(); i++) { + auto& entry = script[i]; + auto& cmd = entry.cmd; + + if (auto* mod = std::get_if(&cmd)) { + if (mod->isDefinition) { + Fatal() << "Module definition is not supported"; + } + auto* w = std::get_if>(&mod->module); + assert(w && "expected parsed Module pointer inside WASTModule"); + + auto wasm = *w; + size_t currentIdx = moduleCounter++; + std::string prefix = "spec_" + std::to_string(currentIdx); + + // Generate separate files for this module + std::string modHFilename = + baseBasename + "." + std::to_string(currentIdx) + ".h"; + + std::string modCPath = basePath + "." + std::to_string(currentIdx) + ".c"; + std::string modHPath = basePath + "." + std::to_string(currentIdx) + ".h"; + + Output modCOut(modCPath, Flags::Text); + Output modHOut(modHPath, Flags::Text); + + Wasm2CBuilder::Flags modFlags = flags; + modFlags.moduleName = prefix; + modFlags.headerName = modHFilename; + + Wasm2CBuilder builder(modFlags); + builder.processWasm(wasm.get(), modCOut.getStream(), modHOut.getStream()); + + c << "#include \"" << modHFilename << "\"" << endl; + c << SpecTop << endl << endl; + + } else if (std::get_if(&cmd)) { + Fatal() << "register is not yet supported"; + } else if (std::get_if(&cmd)) { + Fatal() << "assertions are not yet supported"; + } else { + Fatal() << "unsupported command"; + } + } + + // Write main execution entry point + c << "void run_spec_tests() {" << endl; + c << "}" << endl; +} + +} // namespace wasm diff --git a/src/tools/wasm2c/assertion-emitter.h b/src/tools/wasm2c/assertion-emitter.h new file mode 100644 index 00000000000..4f2edfdcd52 --- /dev/null +++ b/src/tools/wasm2c/assertion-emitter.h @@ -0,0 +1,43 @@ +/* + * Copyright 2026 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef wasm_tools_wasm2c_assertion_emitter_h +#define wasm_tools_wasm2c_assertion_emitter_h + +#include + +#include "parser/wat-parser.h" +#include "tools/wasm2c/wasm2c-builder.h" + +namespace wasm { + +class AssertionEmitter { +public: + AssertionEmitter(WATParser::WASTScript& script, + Wasm2CBuilder::Flags flags); + + void emit(std::ostream& cOut, const std::string& outputCPath); + +private: + WATParser::WASTScript& script; + Wasm2CBuilder::Flags flags; + + size_t moduleCounter = 0; +}; + +} // namespace wasm + +#endif // wasm_tools_wasm2c_assertion_emitter_h diff --git a/src/tools/wasm2c/c-printer.h b/src/tools/wasm2c/c-printer.h new file mode 100644 index 00000000000..561f290088b --- /dev/null +++ b/src/tools/wasm2c/c-printer.h @@ -0,0 +1,60 @@ +/* + * Copyright 2026 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef wasm_tools_wasm2c_c_printer_h +#define wasm_tools_wasm2c_c_printer_h + +#include + +namespace wasm { + +class CPrinter; +typedef CPrinter& (*CPrinterManipulator)(CPrinter&); + +class CPrinter { +public: + std::ostream& out; + int indentLevel = 0; + bool startOfLine = true; + + CPrinter(std::ostream& out) : out(out) {} + + void indent() { indentLevel++; } + void outdent() { indentLevel--; } + + CPrinter& operator<<(CPrinter& (*pf)(CPrinter&)) { return pf(*this); } + + template CPrinter& operator<<(const T& val) { + if (startOfLine) { + for (int i = 0; i < indentLevel * 2; i++) { + out << ' '; + } + startOfLine = false; + } + out << val; + return *this; + } +}; + +inline CPrinter& endl(CPrinter& p) { + p.out << '\n'; + p.startOfLine = true; + return p; +} + +} // namespace wasm + +#endif // wasm_tools_wasm2c_c_printer_h diff --git a/src/tools/wasm2c/gen-wasm2c-templates.cmake b/src/tools/wasm2c/gen-wasm2c-templates.cmake new file mode 100644 index 00000000000..db554543d93 --- /dev/null +++ b/src/tools/wasm2c/gen-wasm2c-templates.cmake @@ -0,0 +1,5 @@ +# https://stackoverflow.com/a/47801116 +file(READ ${in} content) +string(REGEX REPLACE "(.[^\n]*\n)" "R\"w2c_template(\\1)w2c_template\"\n" content "${content}") +set(content "const char* ${symbol} = ${content};\n") +file(WRITE ${out} "${content}") diff --git a/src/tools/wasm2c/prebuilt/wasm2c_atomicops_source_declarations.cpp b/src/tools/wasm2c/prebuilt/wasm2c_atomicops_source_declarations.cpp new file mode 100644 index 00000000000..ec150801dad --- /dev/null +++ b/src/tools/wasm2c/prebuilt/wasm2c_atomicops_source_declarations.cpp @@ -0,0 +1,454 @@ +const char* AtomicsOpsSourceDeclarations = R"w2c_template(#include +)w2c_template" +R"w2c_template( +#ifndef WASM_RT_C11_AVAILABLE +)w2c_template" +R"w2c_template(#error "C11 is required for Wasm threads and shared memory support" +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +#define ATOMIC_ALIGNMENT_CHECK(addr, t1) \ +)w2c_template" +R"w2c_template( if (UNLIKELY(addr % sizeof(t1))) { \ +)w2c_template" +R"w2c_template( TRAP(UNALIGNED); \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( +#define DEFINE_SHARED_LOAD(name, t1, t2, t3, force_read) \ +)w2c_template" +R"w2c_template( static inline t3 name##_unchecked(wasm_rt_shared_memory_t* mem, u64 addr) { \ +)w2c_template" +R"w2c_template( t1 result; \ +)w2c_template" +R"w2c_template( result = atomic_load_explicit( \ +)w2c_template" +R"w2c_template( (_Atomic volatile t1*)MEM_ADDR(mem, addr, sizeof(t1)), \ +)w2c_template" +R"w2c_template( memory_order_relaxed); \ +)w2c_template" +R"w2c_template( force_read(result); \ +)w2c_template" +R"w2c_template( return (t3)(t2)result; \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( DEF_MEM_CHECKS0(name, _shared_, t1, return, t3) +)w2c_template" +R"w2c_template( +DEFINE_SHARED_LOAD(i32_load_shared, u32, u32, u32, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_SHARED_LOAD(i64_load_shared, u64, u64, u64, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_SHARED_LOAD(f32_load_shared, f32, f32, f32, FORCE_READ_FLOAT) +)w2c_template" +R"w2c_template(DEFINE_SHARED_LOAD(f64_load_shared, f64, f64, f64, FORCE_READ_FLOAT) +)w2c_template" +R"w2c_template(DEFINE_SHARED_LOAD(i32_load8_s_shared, s8, s32, u32, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_SHARED_LOAD(i64_load8_s_shared, s8, s64, u64, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_SHARED_LOAD(i32_load8_u_shared, u8, u32, u32, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_SHARED_LOAD(i64_load8_u_shared, u8, u64, u64, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_SHARED_LOAD(i32_load16_s_shared, s16, s32, u32, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_SHARED_LOAD(i64_load16_s_shared, s16, s64, u64, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_SHARED_LOAD(i32_load16_u_shared, u16, u32, u32, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_SHARED_LOAD(i64_load16_u_shared, u16, u64, u64, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_SHARED_LOAD(i64_load32_s_shared, s32, s64, u64, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_SHARED_LOAD(i64_load32_u_shared, u32, u64, u64, FORCE_READ_INT) +)w2c_template" +R"w2c_template( +#define DEFINE_SHARED_STORE(name, t1, t2) \ +)w2c_template" +R"w2c_template( static inline void name##_unchecked(wasm_rt_shared_memory_t* mem, u64 addr, \ +)w2c_template" +R"w2c_template( t2 value) { \ +)w2c_template" +R"w2c_template( t1 wrapped = (t1)value; \ +)w2c_template" +R"w2c_template( atomic_store_explicit( \ +)w2c_template" +R"w2c_template( (_Atomic volatile t1*)MEM_ADDR(mem, addr, sizeof(t1)), wrapped, \ +)w2c_template" +R"w2c_template( memory_order_relaxed); \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( DEF_MEM_CHECKS1(name, _shared_, t1, , void, t2) +)w2c_template" +R"w2c_template( +DEFINE_SHARED_STORE(i32_store_shared, u32, u32) +)w2c_template" +R"w2c_template(DEFINE_SHARED_STORE(i64_store_shared, u64, u64) +)w2c_template" +R"w2c_template(DEFINE_SHARED_STORE(f32_store_shared, f32, f32) +)w2c_template" +R"w2c_template(DEFINE_SHARED_STORE(f64_store_shared, f64, f64) +)w2c_template" +R"w2c_template(DEFINE_SHARED_STORE(i32_store8_shared, u8, u32) +)w2c_template" +R"w2c_template(DEFINE_SHARED_STORE(i32_store16_shared, u16, u32) +)w2c_template" +R"w2c_template(DEFINE_SHARED_STORE(i64_store8_shared, u8, u64) +)w2c_template" +R"w2c_template(DEFINE_SHARED_STORE(i64_store16_shared, u16, u64) +)w2c_template" +R"w2c_template(DEFINE_SHARED_STORE(i64_store32_shared, u32, u64) +)w2c_template" +R"w2c_template( +#define DEFINE_ATOMIC_LOAD(name, t1, t2, t3, force_read) \ +)w2c_template" +R"w2c_template( static inline t3 name##_unchecked(wasm_rt_memory_t* mem, u64 addr) { \ +)w2c_template" +R"w2c_template( ATOMIC_ALIGNMENT_CHECK(addr, t1); \ +)w2c_template" +R"w2c_template( t1 result; \ +)w2c_template" +R"w2c_template( wasm_rt_memcpy(&result, MEM_ADDR(mem, addr, sizeof(t1)), sizeof(t1)); \ +)w2c_template" +R"w2c_template( force_read(result); \ +)w2c_template" +R"w2c_template( return (t3)(t2)result; \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( DEF_MEM_CHECKS0(name, _, t1, return, t3) \ +)w2c_template" +R"w2c_template( static inline t3 name##_shared_unchecked(wasm_rt_shared_memory_t* mem, \ +)w2c_template" +R"w2c_template( u64 addr) { \ +)w2c_template" +R"w2c_template( ATOMIC_ALIGNMENT_CHECK(addr, t1); \ +)w2c_template" +R"w2c_template( t1 result; \ +)w2c_template" +R"w2c_template( result = \ +)w2c_template" +R"w2c_template( atomic_load((_Atomic volatile t1*)MEM_ADDR(mem, addr, sizeof(t1))); \ +)w2c_template" +R"w2c_template( force_read(result); \ +)w2c_template" +R"w2c_template( return (t3)(t2)result; \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( DEF_MEM_CHECKS0(name##_shared, _shared_, t1, return, t3) +)w2c_template" +R"w2c_template( +DEFINE_ATOMIC_LOAD(i32_atomic_load, u32, u32, u32, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_LOAD(i64_atomic_load, u64, u64, u64, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_LOAD(i32_atomic_load8_u, u8, u32, u32, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_LOAD(i64_atomic_load8_u, u8, u64, u64, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_LOAD(i32_atomic_load16_u, u16, u32, u32, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_LOAD(i64_atomic_load16_u, u16, u64, u64, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_LOAD(i64_atomic_load32_u, u32, u64, u64, FORCE_READ_INT) +)w2c_template" +R"w2c_template( +#define DEFINE_ATOMIC_STORE(name, t1, t2) \ +)w2c_template" +R"w2c_template( static inline void name##_unchecked(wasm_rt_memory_t* mem, u64 addr, \ +)w2c_template" +R"w2c_template( t2 value) { \ +)w2c_template" +R"w2c_template( ATOMIC_ALIGNMENT_CHECK(addr, t1); \ +)w2c_template" +R"w2c_template( t1 wrapped = (t1)value; \ +)w2c_template" +R"w2c_template( wasm_rt_memcpy(MEM_ADDR(mem, addr, sizeof(t1)), &wrapped, sizeof(t1)); \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( DEF_MEM_CHECKS1(name, _, t1, , void, t2) \ +)w2c_template" +R"w2c_template( static inline void name##_shared_unchecked(wasm_rt_shared_memory_t* mem, \ +)w2c_template" +R"w2c_template( u64 addr, t2 value) { \ +)w2c_template" +R"w2c_template( ATOMIC_ALIGNMENT_CHECK(addr, t1); \ +)w2c_template" +R"w2c_template( t1 wrapped = (t1)value; \ +)w2c_template" +R"w2c_template( atomic_store((_Atomic volatile t1*)MEM_ADDR(mem, addr, sizeof(t1)), \ +)w2c_template" +R"w2c_template( wrapped); \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( DEF_MEM_CHECKS1(name##_shared, _shared_, t1, , void, t2) +)w2c_template" +R"w2c_template( +DEFINE_ATOMIC_STORE(i32_atomic_store, u32, u32) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_STORE(i64_atomic_store, u64, u64) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_STORE(i32_atomic_store8, u8, u32) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_STORE(i32_atomic_store16, u16, u32) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_STORE(i64_atomic_store8, u8, u64) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_STORE(i64_atomic_store16, u16, u64) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_STORE(i64_atomic_store32, u32, u64) +)w2c_template" +R"w2c_template( +#define DEFINE_ATOMIC_RMW(name, opname, op, t1, t2) \ +)w2c_template" +R"w2c_template( static inline t2 name##_unchecked(wasm_rt_memory_t* mem, u64 addr, \ +)w2c_template" +R"w2c_template( t2 value) { \ +)w2c_template" +R"w2c_template( ATOMIC_ALIGNMENT_CHECK(addr, t1); \ +)w2c_template" +R"w2c_template( t1 wrapped = (t1)value; \ +)w2c_template" +R"w2c_template( t1 ret; \ +)w2c_template" +R"w2c_template( wasm_rt_memcpy(&ret, MEM_ADDR(mem, addr, sizeof(t1)), sizeof(t1)); \ +)w2c_template" +R"w2c_template( ret = ret op wrapped; \ +)w2c_template" +R"w2c_template( wasm_rt_memcpy(MEM_ADDR(mem, addr, sizeof(t1)), &ret, sizeof(t1)); \ +)w2c_template" +R"w2c_template( return (t2)ret; \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( DEF_MEM_CHECKS1(name, _, t1, return, t2, t2) \ +)w2c_template" +R"w2c_template( static inline t2 name##_shared_unchecked(wasm_rt_shared_memory_t* mem, \ +)w2c_template" +R"w2c_template( u64 addr, t2 value) { \ +)w2c_template" +R"w2c_template( ATOMIC_ALIGNMENT_CHECK(addr, t1); \ +)w2c_template" +R"w2c_template( t1 wrapped = (t1)value; \ +)w2c_template" +R"w2c_template( t1 ret = atomic_##opname( \ +)w2c_template" +R"w2c_template( (_Atomic volatile t1*)MEM_ADDR(mem, addr, sizeof(t1)), wrapped); \ +)w2c_template" +R"w2c_template( return (t2)ret; \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( DEF_MEM_CHECKS1(name##_shared, _shared_, t1, return, t2, t2) +)w2c_template" +R"w2c_template( +DEFINE_ATOMIC_RMW(i32_atomic_rmw8_add_u, fetch_add, +, u8, u32) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i32_atomic_rmw16_add_u, fetch_add, +, u16, u32) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i32_atomic_rmw_add, fetch_add, +, u32, u32) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i64_atomic_rmw8_add_u, fetch_add, +, u8, u64) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i64_atomic_rmw16_add_u, fetch_add, +, u16, u64) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i64_atomic_rmw32_add_u, fetch_add, +, u32, u64) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i64_atomic_rmw_add, fetch_add, +, u64, u64) +)w2c_template" +R"w2c_template( +DEFINE_ATOMIC_RMW(i32_atomic_rmw8_sub_u, fetch_sub, -, u8, u32) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i32_atomic_rmw16_sub_u, fetch_sub, -, u16, u32) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i32_atomic_rmw_sub, fetch_sub, -, u32, u32) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i64_atomic_rmw8_sub_u, fetch_sub, -, u8, u64) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i64_atomic_rmw16_sub_u, fetch_sub, -, u16, u64) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i64_atomic_rmw32_sub_u, fetch_sub, -, u32, u64) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i64_atomic_rmw_sub, fetch_sub, -, u64, u64) +)w2c_template" +R"w2c_template( +DEFINE_ATOMIC_RMW(i32_atomic_rmw8_and_u, fetch_and, &, u8, u32) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i32_atomic_rmw16_and_u, fetch_and, &, u16, u32) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i32_atomic_rmw_and, fetch_and, &, u32, u32) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i64_atomic_rmw8_and_u, fetch_and, &, u8, u64) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i64_atomic_rmw16_and_u, fetch_and, &, u16, u64) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i64_atomic_rmw32_and_u, fetch_and, &, u32, u64) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i64_atomic_rmw_and, fetch_and, &, u64, u64) +)w2c_template" +R"w2c_template( +DEFINE_ATOMIC_RMW(i32_atomic_rmw8_or_u, fetch_or, |, u8, u32) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i32_atomic_rmw16_or_u, fetch_or, |, u16, u32) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i32_atomic_rmw_or, fetch_or, |, u32, u32) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i64_atomic_rmw8_or_u, fetch_or, |, u8, u64) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i64_atomic_rmw16_or_u, fetch_or, |, u16, u64) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i64_atomic_rmw32_or_u, fetch_or, |, u32, u64) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i64_atomic_rmw_or, fetch_or, |, u64, u64) +)w2c_template" +R"w2c_template( +DEFINE_ATOMIC_RMW(i32_atomic_rmw8_xor_u, fetch_xor, ^, u8, u32) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i32_atomic_rmw16_xor_u, fetch_xor, ^, u16, u32) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i32_atomic_rmw_xor, fetch_xor, ^, u32, u32) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i64_atomic_rmw8_xor_u, fetch_xor, ^, u8, u64) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i64_atomic_rmw16_xor_u, fetch_xor, ^, u16, u64) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i64_atomic_rmw32_xor_u, fetch_xor, ^, u32, u64) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_RMW(i64_atomic_rmw_xor, fetch_xor, ^, u64, u64) +)w2c_template" +R"w2c_template( +#define DEFINE_ATOMIC_XCHG(name, opname, t1, t2) \ +)w2c_template" +R"w2c_template( static inline t2 name##_unchecked(wasm_rt_memory_t* mem, u64 addr, \ +)w2c_template" +R"w2c_template( t2 value) { \ +)w2c_template" +R"w2c_template( ATOMIC_ALIGNMENT_CHECK(addr, t1); \ +)w2c_template" +R"w2c_template( t1 wrapped = (t1)value; \ +)w2c_template" +R"w2c_template( t1 ret; \ +)w2c_template" +R"w2c_template( wasm_rt_memcpy(&ret, MEM_ADDR(mem, addr, sizeof(t1)), sizeof(t1)); \ +)w2c_template" +R"w2c_template( wasm_rt_memcpy(MEM_ADDR(mem, addr, sizeof(t1)), &wrapped, sizeof(t1)); \ +)w2c_template" +R"w2c_template( return (t2)ret; \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( DEF_MEM_CHECKS1(name, _, t1, return, t2, t2) \ +)w2c_template" +R"w2c_template( static inline t2 name##_shared_unchecked(wasm_rt_shared_memory_t* mem, \ +)w2c_template" +R"w2c_template( u64 addr, t2 value) { \ +)w2c_template" +R"w2c_template( ATOMIC_ALIGNMENT_CHECK(addr, t1); \ +)w2c_template" +R"w2c_template( t1 wrapped = (t1)value; \ +)w2c_template" +R"w2c_template( t1 ret = atomic_##opname( \ +)w2c_template" +R"w2c_template( (_Atomic volatile t1*)MEM_ADDR(mem, addr, sizeof(t1)), wrapped); \ +)w2c_template" +R"w2c_template( return (t2)ret; \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( DEF_MEM_CHECKS1(name##_shared, _shared_, t1, return, t2, t2) +)w2c_template" +R"w2c_template( +DEFINE_ATOMIC_XCHG(i32_atomic_rmw8_xchg_u, exchange, u8, u32) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_XCHG(i32_atomic_rmw16_xchg_u, exchange, u16, u32) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_XCHG(i32_atomic_rmw_xchg, exchange, u32, u32) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_XCHG(i64_atomic_rmw8_xchg_u, exchange, u8, u64) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_XCHG(i64_atomic_rmw16_xchg_u, exchange, u16, u64) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_XCHG(i64_atomic_rmw32_xchg_u, exchange, u32, u64) +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_XCHG(i64_atomic_rmw_xchg, exchange, u64, u64) +)w2c_template" +R"w2c_template( +#define DEFINE_ATOMIC_CMP_XCHG(name, t1, t2) \ +)w2c_template" +R"w2c_template( static inline t1 name##_unchecked(wasm_rt_memory_t* mem, u64 addr, \ +)w2c_template" +R"w2c_template( t1 expected, t1 replacement) { \ +)w2c_template" +R"w2c_template( ATOMIC_ALIGNMENT_CHECK(addr, t2); \ +)w2c_template" +R"w2c_template( t2 expected_wrapped = (t2)expected; \ +)w2c_template" +R"w2c_template( t2 replacement_wrapped = (t2)replacement; \ +)w2c_template" +R"w2c_template( t2 ret; \ +)w2c_template" +R"w2c_template( wasm_rt_memcpy(&ret, MEM_ADDR(mem, addr, sizeof(t2)), sizeof(t2)); \ +)w2c_template" +R"w2c_template( if (ret == expected_wrapped) { \ +)w2c_template" +R"w2c_template( wasm_rt_memcpy(MEM_ADDR(mem, addr, sizeof(t2)), &replacement_wrapped, \ +)w2c_template" +R"w2c_template( sizeof(t2)); \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( return (t1)expected_wrapped; \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( DEF_MEM_CHECKS2(name, _, t2, return, t1, t1, t1) \ +)w2c_template" +R"w2c_template( static inline t1 name##_shared_unchecked( \ +)w2c_template" +R"w2c_template( wasm_rt_shared_memory_t* mem, u64 addr, t1 expected, t1 replacement) { \ +)w2c_template" +R"w2c_template( ATOMIC_ALIGNMENT_CHECK(addr, t2); \ +)w2c_template" +R"w2c_template( t2 expected_wrapped = (t2)expected; \ +)w2c_template" +R"w2c_template( t2 replacement_wrapped = (t2)replacement; \ +)w2c_template" +R"w2c_template( atomic_compare_exchange_strong( \ +)w2c_template" +R"w2c_template( (_Atomic volatile t2*)MEM_ADDR(mem, addr, sizeof(t2)), \ +)w2c_template" +R"w2c_template( &expected_wrapped, replacement_wrapped); \ +)w2c_template" +R"w2c_template( return (t1)expected_wrapped; \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( DEF_MEM_CHECKS2(name##_shared, _shared_, t2, return, t1, t1, t1) +)w2c_template" +R"w2c_template( +DEFINE_ATOMIC_CMP_XCHG(i32_atomic_rmw8_cmpxchg_u, u32, u8); +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_CMP_XCHG(i32_atomic_rmw16_cmpxchg_u, u32, u16); +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_CMP_XCHG(i32_atomic_rmw_cmpxchg, u32, u32); +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_CMP_XCHG(i64_atomic_rmw8_cmpxchg_u, u64, u8); +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_CMP_XCHG(i64_atomic_rmw16_cmpxchg_u, u64, u16); +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_CMP_XCHG(i64_atomic_rmw32_cmpxchg_u, u64, u32); +)w2c_template" +R"w2c_template(DEFINE_ATOMIC_CMP_XCHG(i64_atomic_rmw_cmpxchg, u64, u64); +)w2c_template" +R"w2c_template( +#define atomic_fence() atomic_thread_fence(memory_order_seq_cst) +)w2c_template" +; diff --git a/src/tools/wasm2c/prebuilt/wasm2c_header_bottom.cpp b/src/tools/wasm2c/prebuilt/wasm2c_header_bottom.cpp new file mode 100644 index 00000000000..4028a9cecac --- /dev/null +++ b/src/tools/wasm2c/prebuilt/wasm2c_header_bottom.cpp @@ -0,0 +1,7 @@ +const char* HeaderBottom = R"w2c_template(#ifdef __cplusplus +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template(#endif +)w2c_template" +; diff --git a/src/tools/wasm2c/prebuilt/wasm2c_header_top.cpp b/src/tools/wasm2c/prebuilt/wasm2c_header_top.cpp new file mode 100644 index 00000000000..1a3176ef4f5 --- /dev/null +++ b/src/tools/wasm2c/prebuilt/wasm2c_header_top.cpp @@ -0,0 +1,37 @@ +const char* HeaderTop = R"w2c_template(#include +)w2c_template" +R"w2c_template( +#ifndef WASM_RT_CORE_TYPES_DEFINED +)w2c_template" +R"w2c_template(#define WASM_RT_CORE_TYPES_DEFINED +)w2c_template" +R"w2c_template(typedef uint8_t u8; +)w2c_template" +R"w2c_template(typedef int8_t s8; +)w2c_template" +R"w2c_template(typedef uint16_t u16; +)w2c_template" +R"w2c_template(typedef int16_t s16; +)w2c_template" +R"w2c_template(typedef uint32_t u32; +)w2c_template" +R"w2c_template(typedef int32_t s32; +)w2c_template" +R"w2c_template(typedef uint64_t u64; +)w2c_template" +R"w2c_template(typedef int64_t s64; +)w2c_template" +R"w2c_template(typedef float f32; +)w2c_template" +R"w2c_template(typedef double f64; +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +#ifdef __cplusplus +)w2c_template" +R"w2c_template(extern "C" { +)w2c_template" +R"w2c_template(#endif +)w2c_template" +; diff --git a/src/tools/wasm2c/prebuilt/wasm2c_simd_source_declarations.cpp b/src/tools/wasm2c/prebuilt/wasm2c_simd_source_declarations.cpp new file mode 100644 index 00000000000..b92016b1d96 --- /dev/null +++ b/src/tools/wasm2c/prebuilt/wasm2c_simd_source_declarations.cpp @@ -0,0 +1,672 @@ +const char* SimdSourceDeclarations = R"w2c_template(#if defined(__GNUC__) && defined(__x86_64__) +)w2c_template" +R"w2c_template(#define SIMD_FORCE_READ(var) __asm__("" ::"x"(var)); +)w2c_template" +R"w2c_template(#elif defined(__GNUC__) && defined(__aarch64__) +)w2c_template" +R"w2c_template(#define SIMD_FORCE_READ(var) __asm__("" ::"w"(var)); +)w2c_template" +R"w2c_template(#elif defined(__s390x__) +)w2c_template" +R"w2c_template(#define SIMD_FORCE_READ(var) __asm__("" ::"d"(var)); +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#define SIMD_FORCE_READ(var) +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template(// TODO: equivalent constraint for ARM and other architectures +)w2c_template" +R"w2c_template( +#define DEFINE_SIMD_LOAD_FUNC(name, func, t) \ +)w2c_template" +R"w2c_template( static inline v128 name##_unchecked(wasm_rt_memory_t* mem, u64 addr) { \ +)w2c_template" +R"w2c_template( v128 result = func(MEM_ADDR(mem, addr, sizeof(t))); \ +)w2c_template" +R"w2c_template( SIMD_FORCE_READ(result); \ +)w2c_template" +R"w2c_template( return result; \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( DEF_MEM_CHECKS0(name, _, t, return, v128); +)w2c_template" +R"w2c_template( +#define DEFINE_SIMD_LOAD_LANE(name, func, t, lane) \ +)w2c_template" +R"w2c_template( static inline v128 name##_unchecked(wasm_rt_memory_t* mem, u64 addr, \ +)w2c_template" +R"w2c_template( v128 vec) { \ +)w2c_template" +R"w2c_template( v128 result = func(MEM_ADDR(mem, addr, sizeof(t)), vec, lane); \ +)w2c_template" +R"w2c_template( SIMD_FORCE_READ(result); \ +)w2c_template" +R"w2c_template( return result; \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( DEF_MEM_CHECKS1(name, _, t, return, v128, v128); +)w2c_template" +R"w2c_template( +#define DEFINE_SIMD_STORE(name, t) \ +)w2c_template" +R"w2c_template( static inline void name##_unchecked(wasm_rt_memory_t* mem, u64 addr, \ +)w2c_template" +R"w2c_template( v128 value) { \ +)w2c_template" +R"w2c_template( simde_wasm_v128_store(MEM_ADDR(mem, addr, sizeof(t)), value); \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( DEF_MEM_CHECKS1(name, _, t, , void, v128); +)w2c_template" +R"w2c_template( +#define DEFINE_SIMD_STORE_LANE(name, func, t, lane) \ +)w2c_template" +R"w2c_template( static inline void name##_unchecked(wasm_rt_memory_t* mem, u64 addr, \ +)w2c_template" +R"w2c_template( v128 value) { \ +)w2c_template" +R"w2c_template( func(MEM_ADDR(mem, addr, sizeof(t)), value, lane); \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( DEF_MEM_CHECKS1(name, _, t, , void, v128); +)w2c_template" +R"w2c_template( +// clang-format off +)w2c_template" +R"w2c_template(#if WASM_RT_BIG_ENDIAN +)w2c_template" +R"w2c_template(static inline v128 v128_impl_load32_zero(const void* a) { +)w2c_template" +R"w2c_template( return simde_wasm_i8x16_swizzle( +)w2c_template" +R"w2c_template( simde_wasm_v128_load32_zero(a), +)w2c_template" +R"w2c_template( simde_wasm_i8x16_const(12,13,14,15,8,9,10,11,4,5,6,7,0,1,2,3)); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template(static inline v128 v128_impl_load64_zero(const void* a) { +)w2c_template" +R"w2c_template( return simde_wasm_i8x16_swizzle( +)w2c_template" +R"w2c_template( simde_wasm_v128_load64_zero(a), +)w2c_template" +R"w2c_template( simde_wasm_i8x16_const(8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7)); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#define v128_impl_load32_zero simde_wasm_v128_load32_zero +)w2c_template" +R"w2c_template(#define v128_impl_load64_zero simde_wasm_v128_load64_zero +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +DEFINE_SIMD_LOAD_FUNC(v128_load, simde_wasm_v128_load, v128) +)w2c_template" +R"w2c_template( +DEFINE_SIMD_LOAD_FUNC(v128_load8_splat, simde_wasm_v128_load8_splat, u8) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_FUNC(v128_load16_splat, simde_wasm_v128_load16_splat, u16) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_FUNC(v128_load32_splat, simde_wasm_v128_load32_splat, u32) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_FUNC(v128_load64_splat, simde_wasm_v128_load64_splat, u64) +)w2c_template" +R"w2c_template( +DEFINE_SIMD_LOAD_FUNC(i16x8_load8x8, simde_wasm_i16x8_load8x8, u64) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_FUNC(u16x8_load8x8, simde_wasm_u16x8_load8x8, u64) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_FUNC(i32x4_load16x4, simde_wasm_i32x4_load16x4, u64) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_FUNC(u32x4_load16x4, simde_wasm_u32x4_load16x4, u64) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_FUNC(i64x2_load32x2, simde_wasm_i64x2_load32x2, u64) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_FUNC(u64x2_load32x2, simde_wasm_u64x2_load32x2, u64) +)w2c_template" +R"w2c_template( +DEFINE_SIMD_LOAD_FUNC(v128_load32_zero, v128_impl_load32_zero, u32) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_FUNC(v128_load64_zero, v128_impl_load64_zero, u64) +)w2c_template" +R"w2c_template( +#if WASM_RT_BIG_ENDIAN +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane0, simde_wasm_v128_load8_lane, u8, 15) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane1, simde_wasm_v128_load8_lane, u8, 14) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane2, simde_wasm_v128_load8_lane, u8, 13) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane3, simde_wasm_v128_load8_lane, u8, 12) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane4, simde_wasm_v128_load8_lane, u8, 11) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane5, simde_wasm_v128_load8_lane, u8, 10) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane6, simde_wasm_v128_load8_lane, u8, 9) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane7, simde_wasm_v128_load8_lane, u8, 8) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane8, simde_wasm_v128_load8_lane, u8, 7) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane9, simde_wasm_v128_load8_lane, u8, 6) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane10, simde_wasm_v128_load8_lane, u8, 5) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane11, simde_wasm_v128_load8_lane, u8, 4) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane12, simde_wasm_v128_load8_lane, u8, 3) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane13, simde_wasm_v128_load8_lane, u8, 2) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane14, simde_wasm_v128_load8_lane, u8, 1) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane15, simde_wasm_v128_load8_lane, u8, 0) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane0, simde_wasm_v128_load16_lane, u16, 7) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane1, simde_wasm_v128_load16_lane, u16, 6) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane2, simde_wasm_v128_load16_lane, u16, 5) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane3, simde_wasm_v128_load16_lane, u16, 4) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane4, simde_wasm_v128_load16_lane, u16, 3) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane5, simde_wasm_v128_load16_lane, u16, 2) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane6, simde_wasm_v128_load16_lane, u16, 1) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane7, simde_wasm_v128_load16_lane, u16, 0) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load32_lane0, simde_wasm_v128_load32_lane, u32, 3) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load32_lane1, simde_wasm_v128_load32_lane, u32, 2) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load32_lane2, simde_wasm_v128_load32_lane, u32, 1) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load32_lane3, simde_wasm_v128_load32_lane, u32, 0) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load64_lane0, simde_wasm_v128_load64_lane, u64, 1) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load64_lane1, simde_wasm_v128_load64_lane, u64, 0) +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane0, simde_wasm_v128_load8_lane, u8, 0) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane1, simde_wasm_v128_load8_lane, u8, 1) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane2, simde_wasm_v128_load8_lane, u8, 2) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane3, simde_wasm_v128_load8_lane, u8, 3) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane4, simde_wasm_v128_load8_lane, u8, 4) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane5, simde_wasm_v128_load8_lane, u8, 5) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane6, simde_wasm_v128_load8_lane, u8, 6) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane7, simde_wasm_v128_load8_lane, u8, 7) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane8, simde_wasm_v128_load8_lane, u8, 8) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane9, simde_wasm_v128_load8_lane, u8, 9) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane10, simde_wasm_v128_load8_lane, u8, 10) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane11, simde_wasm_v128_load8_lane, u8, 11) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane12, simde_wasm_v128_load8_lane, u8, 12) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane13, simde_wasm_v128_load8_lane, u8, 13) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane14, simde_wasm_v128_load8_lane, u8, 14) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load8_lane15, simde_wasm_v128_load8_lane, u8, 15) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane0, simde_wasm_v128_load16_lane, u16, 0) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane1, simde_wasm_v128_load16_lane, u16, 1) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane2, simde_wasm_v128_load16_lane, u16, 2) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane3, simde_wasm_v128_load16_lane, u16, 3) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane4, simde_wasm_v128_load16_lane, u16, 4) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane5, simde_wasm_v128_load16_lane, u16, 5) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane6, simde_wasm_v128_load16_lane, u16, 6) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load16_lane7, simde_wasm_v128_load16_lane, u16, 7) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load32_lane0, simde_wasm_v128_load32_lane, u32, 0) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load32_lane1, simde_wasm_v128_load32_lane, u32, 1) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load32_lane2, simde_wasm_v128_load32_lane, u32, 2) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load32_lane3, simde_wasm_v128_load32_lane, u32, 3) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load64_lane0, simde_wasm_v128_load64_lane, u64, 0) +)w2c_template" +R"w2c_template(DEFINE_SIMD_LOAD_LANE(v128_load64_lane1, simde_wasm_v128_load64_lane, u64, 1) +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +DEFINE_SIMD_STORE(v128_store, v128) +)w2c_template" +R"w2c_template( +#if WASM_RT_BIG_ENDIAN +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane0, simde_wasm_v128_store8_lane, u8, 15) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane1, simde_wasm_v128_store8_lane, u8, 14) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane2, simde_wasm_v128_store8_lane, u8, 13) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane3, simde_wasm_v128_store8_lane, u8, 12) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane4, simde_wasm_v128_store8_lane, u8, 11) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane5, simde_wasm_v128_store8_lane, u8, 10) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane6, simde_wasm_v128_store8_lane, u8, 9) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane7, simde_wasm_v128_store8_lane, u8, 8) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane8, simde_wasm_v128_store8_lane, u8, 7) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane9, simde_wasm_v128_store8_lane, u8, 6) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane10, simde_wasm_v128_store8_lane, u8, 5) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane11, simde_wasm_v128_store8_lane, u8, 4) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane12, simde_wasm_v128_store8_lane, u8, 3) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane13, simde_wasm_v128_store8_lane, u8, 2) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane14, simde_wasm_v128_store8_lane, u8, 1) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane15, simde_wasm_v128_store8_lane, u8, 0) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane0, simde_wasm_v128_store16_lane, u16, 7) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane1, simde_wasm_v128_store16_lane, u16, 6) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane2, simde_wasm_v128_store16_lane, u16, 5) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane3, simde_wasm_v128_store16_lane, u16, 4) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane4, simde_wasm_v128_store16_lane, u16, 3) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane5, simde_wasm_v128_store16_lane, u16, 2) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane6, simde_wasm_v128_store16_lane, u16, 1) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane7, simde_wasm_v128_store16_lane, u16, 0) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store32_lane0, simde_wasm_v128_store32_lane, u32, 3) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store32_lane1, simde_wasm_v128_store32_lane, u32, 2) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store32_lane2, simde_wasm_v128_store32_lane, u32, 1) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store32_lane3, simde_wasm_v128_store32_lane, u32, 0) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store64_lane0, simde_wasm_v128_store64_lane, u64, 1) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store64_lane1, simde_wasm_v128_store64_lane, u64, 0) +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane0, simde_wasm_v128_store8_lane, u8, 0) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane1, simde_wasm_v128_store8_lane, u8, 1) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane2, simde_wasm_v128_store8_lane, u8, 2) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane3, simde_wasm_v128_store8_lane, u8, 3) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane4, simde_wasm_v128_store8_lane, u8, 4) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane5, simde_wasm_v128_store8_lane, u8, 5) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane6, simde_wasm_v128_store8_lane, u8, 6) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane7, simde_wasm_v128_store8_lane, u8, 7) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane8, simde_wasm_v128_store8_lane, u8, 8) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane9, simde_wasm_v128_store8_lane, u8, 9) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane10, simde_wasm_v128_store8_lane, u8, 10) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane11, simde_wasm_v128_store8_lane, u8, 11) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane12, simde_wasm_v128_store8_lane, u8, 12) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane13, simde_wasm_v128_store8_lane, u8, 13) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane14, simde_wasm_v128_store8_lane, u8, 14) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store8_lane15, simde_wasm_v128_store8_lane, u8, 15) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane0, simde_wasm_v128_store16_lane, u16, 0) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane1, simde_wasm_v128_store16_lane, u16, 1) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane2, simde_wasm_v128_store16_lane, u16, 2) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane3, simde_wasm_v128_store16_lane, u16, 3) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane4, simde_wasm_v128_store16_lane, u16, 4) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane5, simde_wasm_v128_store16_lane, u16, 5) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane6, simde_wasm_v128_store16_lane, u16, 6) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store16_lane7, simde_wasm_v128_store16_lane, u16, 7) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store32_lane0, simde_wasm_v128_store32_lane, u32, 0) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store32_lane1, simde_wasm_v128_store32_lane, u32, 1) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store32_lane2, simde_wasm_v128_store32_lane, u32, 2) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store32_lane3, simde_wasm_v128_store32_lane, u32, 3) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store64_lane0, simde_wasm_v128_store64_lane, u64, 0) +)w2c_template" +R"w2c_template(DEFINE_SIMD_STORE_LANE(v128_store64_lane1, simde_wasm_v128_store64_lane, u64, 1) +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +#if WASM_RT_BIG_ENDIAN +)w2c_template" +R"w2c_template(#define v128_const(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) simde_wasm_i8x16_const(p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a) +)w2c_template" +R"w2c_template(#define v128_i8x16_extract_lane(v, l) simde_wasm_i8x16_extract_lane(v, 15-(l)) +)w2c_template" +R"w2c_template(#define v128_u8x16_extract_lane(v, l) simde_wasm_u8x16_extract_lane(v, 15-(l)) +)w2c_template" +R"w2c_template(#define v128_i16x8_extract_lane(v, l) simde_wasm_i16x8_extract_lane(v, 7-(l)) +)w2c_template" +R"w2c_template(#define v128_u16x8_extract_lane(v, l) simde_wasm_u16x8_extract_lane(v, 7-(l)) +)w2c_template" +R"w2c_template(#define v128_i32x4_extract_lane(v, l) simde_wasm_i32x4_extract_lane(v, 3-(l)) +)w2c_template" +R"w2c_template(#define v128_i64x2_extract_lane(v, l) simde_wasm_i64x2_extract_lane(v, 1-(l)) +)w2c_template" +R"w2c_template(#define v128_f32x4_extract_lane(v, l) simde_wasm_f32x4_extract_lane(v, 3-(l)) +)w2c_template" +R"w2c_template(#define v128_f64x2_extract_lane(v, l) simde_wasm_f64x2_extract_lane(v, 1-(l)) +)w2c_template" +R"w2c_template(#define v128_i8x16_replace_lane(v, l, x) simde_wasm_i8x16_replace_lane(v, 15-(l), x) +)w2c_template" +R"w2c_template(#define v128_u8x16_replace_lane(v, l, x) simde_wasm_u8x16_replace_lane(v, 15-(l), x) +)w2c_template" +R"w2c_template(#define v128_i16x8_replace_lane(v, l, x) simde_wasm_i16x8_replace_lane(v, 7-(l), x) +)w2c_template" +R"w2c_template(#define v128_u16x8_replace_lane(v, l, x) simde_wasm_u16x8_replace_lane(v, 7-(l), x) +)w2c_template" +R"w2c_template(#define v128_i32x4_replace_lane(v, l, x) simde_wasm_i32x4_replace_lane(v, 3-(l), x) +)w2c_template" +R"w2c_template(#define v128_i64x2_replace_lane(v, l, x) simde_wasm_i64x2_replace_lane(v, 1-(l), x) +)w2c_template" +R"w2c_template(#define v128_f32x4_replace_lane(v, l, x) simde_wasm_f32x4_replace_lane(v, 3-(l), x) +)w2c_template" +R"w2c_template(#define v128_f64x2_replace_lane(v, l, x) simde_wasm_f64x2_replace_lane(v, 1-(l), x) +)w2c_template" +R"w2c_template(#define v128_i8x16_bitmask(v) simde_wasm_i8x16_bitmask(simde_wasm_i8x16_swizzle(v, simde_wasm_i8x16_const(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0))) +)w2c_template" +R"w2c_template(#define v128_i16x8_bitmask(v) simde_wasm_i16x8_bitmask(simde_wasm_i8x16_swizzle(v, simde_wasm_i8x16_const(14,15,12,13,10,11,8,9,6,7,4,5,2,3,0,1))) +)w2c_template" +R"w2c_template(#define v128_i32x4_bitmask(v) simde_wasm_i32x4_bitmask(simde_wasm_i8x16_swizzle(v, simde_wasm_i8x16_const(12,13,14,15,8,9,10,11,4,5,6,7,0,1,2,3))) +)w2c_template" +R"w2c_template(#define v128_i64x2_bitmask(v) simde_wasm_i64x2_bitmask(simde_wasm_i8x16_swizzle(v, simde_wasm_i8x16_const(8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7))) +)w2c_template" +R"w2c_template(#define v128_i8x16_swizzle(v1, v2) simde_wasm_i8x16_swizzle(v1, simde_wasm_v128_xor(v2, simde_wasm_i8x16_splat(15))) +)w2c_template" +R"w2c_template(#define v128_i8x16_shuffle(v1,v2,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) simde_wasm_i8x16_shuffle(v2,v1,31-(p),31-(o),31-(n),31-(m),31-(l),31-(k),31-(j),31-(i),31-(h),31-(g),31-(f),31-(e),31-(d),31-(c),31-(b),31-(a)) +)w2c_template" +R"w2c_template(#define v128_i16x8_extmul_high_i8x16 simde_wasm_i16x8_extmul_low_i8x16 +)w2c_template" +R"w2c_template(#define v128_u16x8_extmul_high_u8x16 simde_wasm_u16x8_extmul_low_u8x16 +)w2c_template" +R"w2c_template(#define v128_i16x8_extmul_low_i8x16 simde_wasm_i16x8_extmul_high_i8x16 +)w2c_template" +R"w2c_template(#define v128_u16x8_extmul_low_u8x16 simde_wasm_u16x8_extmul_high_u8x16 +)w2c_template" +R"w2c_template(#define v128_i32x4_extmul_high_i16x8 simde_wasm_i32x4_extmul_low_i16x8 +)w2c_template" +R"w2c_template(#define v128_u32x4_extmul_high_u16x8 simde_wasm_u32x4_extmul_low_u16x8 +)w2c_template" +R"w2c_template(#define v128_i32x4_extmul_low_i16x8 simde_wasm_i32x4_extmul_high_i16x8 +)w2c_template" +R"w2c_template(#define v128_u32x4_extmul_low_u16x8 simde_wasm_u32x4_extmul_high_u16x8 +)w2c_template" +R"w2c_template(#define v128_i64x2_extmul_high_i32x4 simde_wasm_i64x2_extmul_low_i32x4 +)w2c_template" +R"w2c_template(#define v128_u64x2_extmul_high_u32x4 simde_wasm_u64x2_extmul_low_u32x4 +)w2c_template" +R"w2c_template(#define v128_i64x2_extmul_low_i32x4 simde_wasm_i64x2_extmul_high_i32x4 +)w2c_template" +R"w2c_template(#define v128_u64x2_extmul_low_u32x4 simde_wasm_u64x2_extmul_high_u32x4 +)w2c_template" +R"w2c_template(#define v128_i16x8_extend_high_i8x16 simde_wasm_i16x8_extend_low_i8x16 +)w2c_template" +R"w2c_template(#define v128_u16x8_extend_high_u8x16 simde_wasm_u16x8_extend_low_u8x16 +)w2c_template" +R"w2c_template(#define v128_i16x8_extend_low_i8x16 simde_wasm_i16x8_extend_high_i8x16 +)w2c_template" +R"w2c_template(#define v128_u16x8_extend_low_u8x16 simde_wasm_u16x8_extend_high_u8x16 +)w2c_template" +R"w2c_template(#define v128_i32x4_extend_high_i16x8 simde_wasm_i32x4_extend_low_i16x8 +)w2c_template" +R"w2c_template(#define v128_u32x4_extend_high_u16x8 simde_wasm_u32x4_extend_low_u16x8 +)w2c_template" +R"w2c_template(#define v128_i32x4_extend_low_i16x8 simde_wasm_i32x4_extend_high_i16x8 +)w2c_template" +R"w2c_template(#define v128_u32x4_extend_low_u16x8 simde_wasm_u32x4_extend_high_u16x8 +)w2c_template" +R"w2c_template(#define v128_i64x2_extend_high_i32x4 simde_wasm_i64x2_extend_low_i32x4 +)w2c_template" +R"w2c_template(#define v128_u64x2_extend_high_u32x4 simde_wasm_u64x2_extend_low_u32x4 +)w2c_template" +R"w2c_template(#define v128_i64x2_extend_low_i32x4 simde_wasm_i64x2_extend_high_i32x4 +)w2c_template" +R"w2c_template(#define v128_u64x2_extend_low_u32x4 simde_wasm_u64x2_extend_high_u32x4 +)w2c_template" +R"w2c_template(#define v128_i32x4_trunc_sat_f64x2_zero(a) \ +)w2c_template" +R"w2c_template( simde_wasm_i8x16_swizzle( \ +)w2c_template" +R"w2c_template( simde_wasm_i32x4_trunc_sat_f64x2_zero(a), \ +)w2c_template" +R"w2c_template( simde_wasm_i8x16_const(8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7)) +)w2c_template" +R"w2c_template(#define v128_u32x4_trunc_sat_f64x2_zero(a) \ +)w2c_template" +R"w2c_template( simde_wasm_i8x16_swizzle( \ +)w2c_template" +R"w2c_template( simde_wasm_u32x4_trunc_sat_f64x2_zero(a), \ +)w2c_template" +R"w2c_template( simde_wasm_i8x16_const(8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7)) +)w2c_template" +R"w2c_template(#define v128_i16x8_narrow_i32x4(a,b) simde_wasm_i16x8_narrow_i32x4(b,a) +)w2c_template" +R"w2c_template(#define v128_u16x8_narrow_i32x4(a,b) simde_wasm_u16x8_narrow_i32x4(b,a) +)w2c_template" +R"w2c_template(#define v128_i8x16_narrow_i16x8(a,b) simde_wasm_i8x16_narrow_i16x8(b,a) +)w2c_template" +R"w2c_template(#define v128_u8x16_narrow_i16x8(a,b) simde_wasm_u8x16_narrow_i16x8(b,a) +)w2c_template" +R"w2c_template(#define v128_f64x2_promote_low_f32x4(a) \ +)w2c_template" +R"w2c_template( simde_wasm_f64x2_promote_low_f32x4(simde_wasm_i8x16_swizzle( \ +)w2c_template" +R"w2c_template( a, \ +)w2c_template" +R"w2c_template( simde_wasm_i8x16_const(8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7))) +)w2c_template" +R"w2c_template(#define v128_f32x4_demote_f64x2_zero(a) \ +)w2c_template" +R"w2c_template( simde_wasm_i8x16_swizzle( \ +)w2c_template" +R"w2c_template( simde_wasm_f32x4_demote_f64x2_zero(a), \ +)w2c_template" +R"w2c_template( simde_wasm_i8x16_const(8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7)) +)w2c_template" +R"w2c_template(#define v128_f64x2_convert_low_i32x4(a) \ +)w2c_template" +R"w2c_template( simde_wasm_f64x2_convert_low_i32x4(simde_wasm_i8x16_swizzle( \ +)w2c_template" +R"w2c_template( a, \ +)w2c_template" +R"w2c_template( simde_wasm_i8x16_const(8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7))) +)w2c_template" +R"w2c_template(#define v128_f64x2_convert_low_u32x4(a) \ +)w2c_template" +R"w2c_template( simde_wasm_f64x2_convert_low_u32x4(simde_wasm_i8x16_swizzle( \ +)w2c_template" +R"w2c_template( a, \ +)w2c_template" +R"w2c_template( simde_wasm_i8x16_const(8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7))) +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#define v128_const simde_wasm_i8x16_const +)w2c_template" +R"w2c_template(#define v128_i8x16_extract_lane simde_wasm_i8x16_extract_lane +)w2c_template" +R"w2c_template(#define v128_u8x16_extract_lane simde_wasm_u8x16_extract_lane +)w2c_template" +R"w2c_template(#define v128_i16x8_extract_lane simde_wasm_i16x8_extract_lane +)w2c_template" +R"w2c_template(#define v128_u16x8_extract_lane simde_wasm_u16x8_extract_lane +)w2c_template" +R"w2c_template(#define v128_i32x4_extract_lane simde_wasm_i32x4_extract_lane +)w2c_template" +R"w2c_template(#define v128_i64x2_extract_lane simde_wasm_i64x2_extract_lane +)w2c_template" +R"w2c_template(#define v128_f32x4_extract_lane simde_wasm_f32x4_extract_lane +)w2c_template" +R"w2c_template(#define v128_f64x2_extract_lane simde_wasm_f64x2_extract_lane +)w2c_template" +R"w2c_template(#define v128_i8x16_replace_lane simde_wasm_i8x16_replace_lane +)w2c_template" +R"w2c_template(#define v128_u8x16_replace_lane simde_wasm_u8x16_replace_lane +)w2c_template" +R"w2c_template(#define v128_i16x8_replace_lane simde_wasm_i16x8_replace_lane +)w2c_template" +R"w2c_template(#define v128_u16x8_replace_lane simde_wasm_u16x8_replace_lane +)w2c_template" +R"w2c_template(#define v128_i32x4_replace_lane simde_wasm_i32x4_replace_lane +)w2c_template" +R"w2c_template(#define v128_i64x2_replace_lane simde_wasm_i64x2_replace_lane +)w2c_template" +R"w2c_template(#define v128_f32x4_replace_lane simde_wasm_f32x4_replace_lane +)w2c_template" +R"w2c_template(#define v128_f64x2_replace_lane simde_wasm_f64x2_replace_lane +)w2c_template" +R"w2c_template(#define v128_i8x16_bitmask simde_wasm_i8x16_bitmask +)w2c_template" +R"w2c_template(#define v128_i16x8_bitmask simde_wasm_i16x8_bitmask +)w2c_template" +R"w2c_template(#define v128_i32x4_bitmask simde_wasm_i32x4_bitmask +)w2c_template" +R"w2c_template(#define v128_i64x2_bitmask simde_wasm_i64x2_bitmask +)w2c_template" +R"w2c_template(#define v128_i8x16_swizzle simde_wasm_i8x16_swizzle +)w2c_template" +R"w2c_template(#define v128_i8x16_shuffle simde_wasm_i8x16_shuffle +)w2c_template" +R"w2c_template(#define v128_i16x8_extmul_high_i8x16 simde_wasm_i16x8_extmul_high_i8x16 +)w2c_template" +R"w2c_template(#define v128_u16x8_extmul_high_u8x16 simde_wasm_u16x8_extmul_high_u8x16 +)w2c_template" +R"w2c_template(#define v128_i16x8_extmul_low_i8x16 simde_wasm_i16x8_extmul_low_i8x16 +)w2c_template" +R"w2c_template(#define v128_u16x8_extmul_low_u8x16 simde_wasm_u16x8_extmul_low_u8x16 +)w2c_template" +R"w2c_template(#define v128_i32x4_extmul_high_i16x8 simde_wasm_i32x4_extmul_high_i16x8 +)w2c_template" +R"w2c_template(#define v128_u32x4_extmul_high_u16x8 simde_wasm_u32x4_extmul_high_u16x8 +)w2c_template" +R"w2c_template(#define v128_i32x4_extmul_low_i16x8 simde_wasm_i32x4_extmul_low_i16x8 +)w2c_template" +R"w2c_template(#define v128_u32x4_extmul_low_u16x8 simde_wasm_u32x4_extmul_low_u16x8 +)w2c_template" +R"w2c_template(#define v128_i64x2_extmul_high_i32x4 simde_wasm_i64x2_extmul_high_i32x4 +)w2c_template" +R"w2c_template(#define v128_u64x2_extmul_high_u32x4 simde_wasm_u64x2_extmul_high_u32x4 +)w2c_template" +R"w2c_template(#define v128_i64x2_extmul_low_i32x4 simde_wasm_i64x2_extmul_low_i32x4 +)w2c_template" +R"w2c_template(#define v128_u64x2_extmul_low_u32x4 simde_wasm_u64x2_extmul_low_u32x4 +)w2c_template" +R"w2c_template(#define v128_i16x8_extend_high_i8x16 simde_wasm_i16x8_extend_high_i8x16 +)w2c_template" +R"w2c_template(#define v128_u16x8_extend_high_u8x16 simde_wasm_u16x8_extend_high_u8x16 +)w2c_template" +R"w2c_template(#define v128_i16x8_extend_low_i8x16 simde_wasm_i16x8_extend_low_i8x16 +)w2c_template" +R"w2c_template(#define v128_u16x8_extend_low_u8x16 simde_wasm_u16x8_extend_low_u8x16 +)w2c_template" +R"w2c_template(#define v128_i32x4_extend_high_i16x8 simde_wasm_i32x4_extend_high_i16x8 +)w2c_template" +R"w2c_template(#define v128_u32x4_extend_high_u16x8 simde_wasm_u32x4_extend_high_u16x8 +)w2c_template" +R"w2c_template(#define v128_i32x4_extend_low_i16x8 simde_wasm_i32x4_extend_low_i16x8 +)w2c_template" +R"w2c_template(#define v128_u32x4_extend_low_u16x8 simde_wasm_u32x4_extend_low_u16x8 +)w2c_template" +R"w2c_template(#define v128_i64x2_extend_high_i32x4 simde_wasm_i64x2_extend_high_i32x4 +)w2c_template" +R"w2c_template(#define v128_u64x2_extend_high_u32x4 simde_wasm_u64x2_extend_high_u32x4 +)w2c_template" +R"w2c_template(#define v128_i64x2_extend_low_i32x4 simde_wasm_i64x2_extend_low_i32x4 +)w2c_template" +R"w2c_template(#define v128_u64x2_extend_low_u32x4 simde_wasm_u64x2_extend_low_u32x4 +)w2c_template" +R"w2c_template(#define v128_i32x4_trunc_sat_f64x2_zero simde_wasm_i32x4_trunc_sat_f64x2_zero +)w2c_template" +R"w2c_template(#define v128_u32x4_trunc_sat_f64x2_zero simde_wasm_u32x4_trunc_sat_f64x2_zero +)w2c_template" +R"w2c_template(#define v128_i16x8_narrow_i32x4 simde_wasm_i16x8_narrow_i32x4 +)w2c_template" +R"w2c_template(#define v128_u16x8_narrow_i32x4 simde_wasm_u16x8_narrow_i32x4 +)w2c_template" +R"w2c_template(#define v128_i8x16_narrow_i16x8 simde_wasm_i8x16_narrow_i16x8 +)w2c_template" +R"w2c_template(#define v128_u8x16_narrow_i16x8 simde_wasm_u8x16_narrow_i16x8 +)w2c_template" +R"w2c_template(#define v128_f64x2_promote_low_f32x4 simde_wasm_f64x2_promote_low_f32x4 +)w2c_template" +R"w2c_template(#define v128_f32x4_demote_f64x2_zero simde_wasm_f32x4_demote_f64x2_zero +)w2c_template" +R"w2c_template(#define v128_f64x2_convert_low_i32x4 simde_wasm_f64x2_convert_low_i32x4 +)w2c_template" +R"w2c_template(#define v128_f64x2_convert_low_u32x4 simde_wasm_f64x2_convert_low_u32x4 +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template(// clang-format on +)w2c_template" +; diff --git a/src/tools/wasm2c/prebuilt/wasm2c_source_declarations.cpp b/src/tools/wasm2c/prebuilt/wasm2c_source_declarations.cpp new file mode 100644 index 00000000000..26c386e0bb1 --- /dev/null +++ b/src/tools/wasm2c/prebuilt/wasm2c_source_declarations.cpp @@ -0,0 +1,1357 @@ +const char* SourceDeclarations = R"w2c_template( +// Computes a pointer to an object of the given size in a little-endian memory. +)w2c_template" +R"w2c_template(// +)w2c_template" +R"w2c_template(// On a little-endian host, this is just &mem->data[addr] - the object's size is +)w2c_template" +R"w2c_template(// unused. On a big-endian host, it's &mem->data[mem->size - addr - n], where n +)w2c_template" +R"w2c_template(// is the object's size. +)w2c_template" +R"w2c_template(// +)w2c_template" +R"w2c_template(// Note that mem may be evaluated multiple times. +)w2c_template" +R"w2c_template(// +)w2c_template" +R"w2c_template(// Parameters: +)w2c_template" +R"w2c_template(// mem - The memory. +)w2c_template" +R"w2c_template(// addr - The address. +)w2c_template" +R"w2c_template(// n - The size of the object. +)w2c_template" +R"w2c_template(// +)w2c_template" +R"w2c_template(// Result: +)w2c_template" +R"w2c_template(// A pointer for an object of size n. +)w2c_template" +R"w2c_template(#if WASM_RT_BIG_ENDIAN +)w2c_template" +R"w2c_template(#define MEM_ADDR(mem, addr, n) ((mem)->data_end - (addr) - (n)) +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#define MEM_ADDR(mem, addr, n) &((mem)->data[addr]) +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +// We can only use Segue for this module if it uses a single unshared imported +)w2c_template" +R"w2c_template(// or exported memory +)w2c_template" +R"w2c_template(#if WASM_RT_USE_SEGUE && IS_SINGLE_UNSHARED_MEMORY +)w2c_template" +R"w2c_template(#define WASM_RT_USE_SEGUE_FOR_THIS_MODULE 1 +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#define WASM_RT_USE_SEGUE_FOR_THIS_MODULE 0 +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +#if WASM_RT_USE_SEGUE_FOR_THIS_MODULE +)w2c_template" +R"w2c_template(// POSIX uses FS for TLS, GS is free +)w2c_template" +R"w2c_template(static inline void* wasm_rt_segue_read_base() { +)w2c_template" +R"w2c_template( if (wasm_rt_fsgsbase_inst_supported) { +)w2c_template" +R"w2c_template( return (void*)__builtin_ia32_rdgsbase64(); +)w2c_template" +R"w2c_template( } else { +)w2c_template" +R"w2c_template( return wasm_rt_syscall_get_segue_base(); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template(static inline void wasm_rt_segue_write_base(void* base) { +)w2c_template" +R"w2c_template(#if WASM_RT_SEGUE_FREE_SEGMENT +)w2c_template" +R"w2c_template( if (wasm_rt_last_segment_val == base) { +)w2c_template" +R"w2c_template( return; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( + wasm_rt_last_segment_val = base; +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( + if (wasm_rt_fsgsbase_inst_supported) { +)w2c_template" +R"w2c_template( __builtin_ia32_wrgsbase64((uintptr_t)base); +)w2c_template" +R"w2c_template( } else { +)w2c_template" +R"w2c_template( wasm_rt_syscall_set_segue_base(base); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template(#define MEM_ADDR_MEMOP(mem, addr, n) ((uint8_t __seg_gs*)(uintptr_t)addr) +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#define MEM_ADDR_MEMOP(mem, addr, n) MEM_ADDR(mem, addr, n) +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +#define TRAP(x) (wasm_rt_trap(WASM_RT_TRAP_##x), 0) +)w2c_template" +R"w2c_template( +#if WASM_RT_STACK_DEPTH_COUNT +)w2c_template" +R"w2c_template(#define FUNC_PROLOGUE \ +)w2c_template" +R"w2c_template( if (++wasm_rt_call_stack_depth > WASM_RT_MAX_CALL_STACK_DEPTH) \ +)w2c_template" +R"w2c_template( TRAP(EXHAUSTION); +)w2c_template" +R"w2c_template( +#define FUNC_EPILOGUE --wasm_rt_call_stack_depth +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#define FUNC_PROLOGUE +)w2c_template" +R"w2c_template( +#define FUNC_EPILOGUE +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +#define UNREACHABLE TRAP(UNREACHABLE) +)w2c_template" +R"w2c_template( +static inline bool func_types_eq(const wasm_rt_func_type_t a, +)w2c_template" +R"w2c_template( const wasm_rt_func_type_t b) { +)w2c_template" +R"w2c_template( return (a == b) || LIKELY(a && b && !memcmp(a, b, 32)); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +#define CHECK_CALL_INDIRECT(table, ft, x) \ +)w2c_template" +R"w2c_template( (LIKELY((x) < table.size && table.data[x].func && \ +)w2c_template" +R"w2c_template( func_types_eq(ft, table.data[x].func_type)) || \ +)w2c_template" +R"w2c_template( TRAP(CALL_INDIRECT)) +)w2c_template" +R"w2c_template( +#define DO_CALL_INDIRECT(table, t, x, ...) ((t)table.data[x].func)(__VA_ARGS__) +)w2c_template" +R"w2c_template( +#define CALL_INDIRECT(table, t, ft, x, ...) \ +)w2c_template" +R"w2c_template( (CHECK_CALL_INDIRECT(table, ft, x), \ +)w2c_template" +R"w2c_template( DO_CALL_INDIRECT(table, t, x, __VA_ARGS__)) +)w2c_template" +R"w2c_template( +static inline bool add_overflow(uint64_t a, uint64_t b, uint64_t* resptr) { +)w2c_template" +R"w2c_template(#if __has_builtin(__builtin_add_overflow) +)w2c_template" +R"w2c_template( return __builtin_add_overflow(a, b, resptr); +)w2c_template" +R"w2c_template(#elif defined(_MSC_VER) +)w2c_template" +R"w2c_template( return _addcarry_u64(0, a, b, resptr); +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#error "Missing implementation of __builtin_add_overflow or _addcarry_u64" +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +#define RANGE_CHECK(mem, offset, len) \ +)w2c_template" +R"w2c_template( do { \ +)w2c_template" +R"w2c_template( uint64_t res; \ +)w2c_template" +R"w2c_template( if (UNLIKELY(add_overflow(offset, len, &res))) \ +)w2c_template" +R"w2c_template( TRAP(OOB); \ +)w2c_template" +R"w2c_template( if (UNLIKELY(res > (mem)->size)) \ +)w2c_template" +R"w2c_template( TRAP(OOB); \ +)w2c_template" +R"w2c_template( } while (0); +)w2c_template" +R"w2c_template( +#if WASM_RT_USE_SEGUE_FOR_THIS_MODULE && WASM_RT_SANITY_CHECKS +)w2c_template" +R"w2c_template(#include +)w2c_template" +R"w2c_template(#define WASM_RT_CHECK_BASE(mem) \ +)w2c_template" +R"w2c_template( if (((uintptr_t)((mem)->data)) != ((uintptr_t)wasm_rt_segue_read_base())) { \ +)w2c_template" +R"w2c_template( puts("Segment register mismatch\n"); \ +)w2c_template" +R"w2c_template( abort(); \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#define WASM_RT_CHECK_BASE(mem) +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +// MEMCHECK_DEFAULT32 is an "accelerated" MEMCHECK used only for +)w2c_template" +R"w2c_template(// default-page-size, 32-bit memories. It may do nothing at all +)w2c_template" +R"w2c_template(// (if hardware bounds-checking is enabled via guard pages) +)w2c_template" +R"w2c_template(// or it may do a slightly faster RANGE_CHECK. +)w2c_template" +R"w2c_template(#if WASM_RT_MEMCHECK_GUARD_PAGES +)w2c_template" +R"w2c_template(#define MEMCHECK_DEFAULT32(mem, a, t) WASM_RT_CHECK_BASE(mem); +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#define MEMCHECK_DEFAULT32(mem, a, t) \ +)w2c_template" +R"w2c_template( WASM_RT_CHECK_BASE(mem); \ +)w2c_template" +R"w2c_template( if (UNLIKELY(a + (uint64_t)sizeof(t) > mem->size)) \ +)w2c_template" +R"w2c_template( TRAP(OOB); +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +// MEMCHECK_GENERAL can be used for any memory +)w2c_template" +R"w2c_template(#define MEMCHECK_GENERAL(mem, a, t) \ +)w2c_template" +R"w2c_template( WASM_RT_CHECK_BASE(mem); \ +)w2c_template" +R"w2c_template( RANGE_CHECK(mem, a, sizeof(t)); +)w2c_template" +R"w2c_template( +#ifdef __GNUC__ +)w2c_template" +R"w2c_template(#define FORCE_READ_INT(var) __asm__("" ::"r"(var)); +)w2c_template" +R"w2c_template(// Clang on Mips requires "f" constraints on floats +)w2c_template" +R"w2c_template(// See https://github.com/llvm/llvm-project/issues/64241 +)w2c_template" +R"w2c_template(#if defined(__clang__) && \ +)w2c_template" +R"w2c_template( (defined(mips) || defined(__mips__) || defined(__mips)) +)w2c_template" +R"w2c_template(#define FORCE_READ_FLOAT(var) __asm__("" ::"f"(var)); +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#define FORCE_READ_FLOAT(var) __asm__("" ::"r"(var)); +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#define FORCE_READ_INT(var) +)w2c_template" +R"w2c_template(#define FORCE_READ_FLOAT(var) +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +static inline void load_data(u8* dest, const u8* src, size_t n) { +)w2c_template" +R"w2c_template( if (!n) { +)w2c_template" +R"w2c_template( return; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template(#if WASM_RT_BIG_ENDIAN +)w2c_template" +R"w2c_template( for (size_t i = 0; i < n; i++) { +)w2c_template" +R"w2c_template( dest[i] = src[n - i - 1]; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template( wasm_rt_memcpy(dest, src, n); +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +#define LOAD_DATA(m, o, i, s) \ +)w2c_template" +R"w2c_template( do { \ +)w2c_template" +R"w2c_template( RANGE_CHECK((&m), o, s); \ +)w2c_template" +R"w2c_template( load_data(MEM_ADDR(&m, o, s), i, s); \ +)w2c_template" +R"w2c_template( } while (0) +)w2c_template" +R"w2c_template( +#define DEF_MEM_CHECKS0(name, shared, mem_type, ret_kw, return_type) \ +)w2c_template" +R"w2c_template( static inline return_type name##_default32(wasm_rt##shared##memory_t* mem, \ +)w2c_template" +R"w2c_template( u64 addr) { \ +)w2c_template" +R"w2c_template( MEMCHECK_DEFAULT32(mem, addr, mem_type); \ +)w2c_template" +R"w2c_template( ret_kw name##_unchecked(mem, addr); \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( static inline return_type name(wasm_rt##shared##memory_t* mem, u64 addr) { \ +)w2c_template" +R"w2c_template( MEMCHECK_GENERAL(mem, addr, mem_type); \ +)w2c_template" +R"w2c_template( ret_kw name##_unchecked(mem, addr); \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( +#define DEF_MEM_CHECKS1(name, shared, mem_type, ret_kw, return_type, \ +)w2c_template" +R"w2c_template( val_type1) \ +)w2c_template" +R"w2c_template( static inline return_type name##_default32(wasm_rt##shared##memory_t* mem, \ +)w2c_template" +R"w2c_template( u64 addr, val_type1 val1) { \ +)w2c_template" +R"w2c_template( MEMCHECK_DEFAULT32(mem, addr, mem_type); \ +)w2c_template" +R"w2c_template( ret_kw name##_unchecked(mem, addr, val1); \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( static inline return_type name(wasm_rt##shared##memory_t* mem, u64 addr, \ +)w2c_template" +R"w2c_template( val_type1 val1) { \ +)w2c_template" +R"w2c_template( MEMCHECK_GENERAL(mem, addr, mem_type); \ +)w2c_template" +R"w2c_template( ret_kw name##_unchecked(mem, addr, val1); \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( +#define DEF_MEM_CHECKS2(name, shared, mem_type, ret_kw, return_type, \ +)w2c_template" +R"w2c_template( val_type1, val_type2) \ +)w2c_template" +R"w2c_template( static inline return_type name##_default32(wasm_rt##shared##memory_t* mem, \ +)w2c_template" +R"w2c_template( u64 addr, val_type1 val1, \ +)w2c_template" +R"w2c_template( val_type2 val2) { \ +)w2c_template" +R"w2c_template( MEMCHECK_DEFAULT32(mem, addr, mem_type); \ +)w2c_template" +R"w2c_template( ret_kw name##_unchecked(mem, addr, val1, val2); \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( static inline return_type name(wasm_rt##shared##memory_t* mem, u64 addr, \ +)w2c_template" +R"w2c_template( val_type1 val1, val_type2 val2) { \ +)w2c_template" +R"w2c_template( MEMCHECK_GENERAL(mem, addr, mem_type); \ +)w2c_template" +R"w2c_template( ret_kw name##_unchecked(mem, addr, val1, val2); \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( +#define DEFINE_LOAD(name, t1, t2, t3, force_read) \ +)w2c_template" +R"w2c_template( static inline t3 name##_unchecked(wasm_rt_memory_t* mem, u64 addr) { \ +)w2c_template" +R"w2c_template( t1 result; \ +)w2c_template" +R"w2c_template( wasm_rt_memcpy(&result, MEM_ADDR_MEMOP(mem, addr, sizeof(t1)), \ +)w2c_template" +R"w2c_template( sizeof(t1)); \ +)w2c_template" +R"w2c_template( force_read(result); \ +)w2c_template" +R"w2c_template( return (t3)(t2)result; \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( DEF_MEM_CHECKS0(name, _, t1, return, t3) +)w2c_template" +R"w2c_template( +#define DEFINE_STORE(name, t1, t2) \ +)w2c_template" +R"w2c_template( static inline void name##_unchecked(wasm_rt_memory_t* mem, u64 addr, \ +)w2c_template" +R"w2c_template( t2 value) { \ +)w2c_template" +R"w2c_template( t1 wrapped = (t1)value; \ +)w2c_template" +R"w2c_template( wasm_rt_memcpy(MEM_ADDR_MEMOP(mem, addr, sizeof(t1)), &wrapped, \ +)w2c_template" +R"w2c_template( sizeof(t1)); \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( DEF_MEM_CHECKS1(name, _, t1, , void, t2) +)w2c_template" +R"w2c_template( +DEFINE_LOAD(i32_load, u32, u32, u32, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_LOAD(i64_load, u64, u64, u64, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_LOAD(f32_load, f32, f32, f32, FORCE_READ_FLOAT) +)w2c_template" +R"w2c_template(DEFINE_LOAD(f64_load, f64, f64, f64, FORCE_READ_FLOAT) +)w2c_template" +R"w2c_template(DEFINE_LOAD(i32_load8_s, s8, s32, u32, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_LOAD(i64_load8_s, s8, s64, u64, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_LOAD(i32_load8_u, u8, u32, u32, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_LOAD(i64_load8_u, u8, u64, u64, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_LOAD(i32_load16_s, s16, s32, u32, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_LOAD(i64_load16_s, s16, s64, u64, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_LOAD(i32_load16_u, u16, u32, u32, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_LOAD(i64_load16_u, u16, u64, u64, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_LOAD(i64_load32_s, s32, s64, u64, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_LOAD(i64_load32_u, u32, u64, u64, FORCE_READ_INT) +)w2c_template" +R"w2c_template(DEFINE_STORE(i32_store, u32, u32) +)w2c_template" +R"w2c_template(DEFINE_STORE(i64_store, u64, u64) +)w2c_template" +R"w2c_template(DEFINE_STORE(f32_store, f32, f32) +)w2c_template" +R"w2c_template(DEFINE_STORE(f64_store, f64, f64) +)w2c_template" +R"w2c_template(DEFINE_STORE(i32_store8, u8, u32) +)w2c_template" +R"w2c_template(DEFINE_STORE(i32_store16, u16, u32) +)w2c_template" +R"w2c_template(DEFINE_STORE(i64_store8, u8, u64) +)w2c_template" +R"w2c_template(DEFINE_STORE(i64_store16, u16, u64) +)w2c_template" +R"w2c_template(DEFINE_STORE(i64_store32, u32, u64) +)w2c_template" +R"w2c_template( +#if defined(_MSC_VER) +)w2c_template" +R"w2c_template( +// Adapted from +)w2c_template" +R"w2c_template(// https://github.com/nemequ/portable-snippets/blob/master/builtin/builtin.h +)w2c_template" +R"w2c_template( +static inline int I64_CLZ(unsigned long long v) { +)w2c_template" +R"w2c_template( unsigned long r = 0; +)w2c_template" +R"w2c_template(#if defined(_M_AMD64) || defined(_M_ARM) +)w2c_template" +R"w2c_template( if (_BitScanReverse64(&r, v)) { +)w2c_template" +R"w2c_template( return 63 - r; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template( if (_BitScanReverse(&r, (unsigned long)(v >> 32))) { +)w2c_template" +R"w2c_template( return 31 - r; +)w2c_template" +R"w2c_template( } else if (_BitScanReverse(&r, (unsigned long)v)) { +)w2c_template" +R"w2c_template( return 63 - r; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( return 64; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static inline int I32_CLZ(unsigned long v) { +)w2c_template" +R"w2c_template( unsigned long r = 0; +)w2c_template" +R"w2c_template( if (_BitScanReverse(&r, v)) { +)w2c_template" +R"w2c_template( return 31 - r; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return 32; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static inline int I64_CTZ(unsigned long long v) { +)w2c_template" +R"w2c_template( if (!v) { +)w2c_template" +R"w2c_template( return 64; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( unsigned long r = 0; +)w2c_template" +R"w2c_template(#if defined(_M_AMD64) || defined(_M_ARM) +)w2c_template" +R"w2c_template( _BitScanForward64(&r, v); +)w2c_template" +R"w2c_template( return (int)r; +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template( if (_BitScanForward(&r, (unsigned int)(v))) { +)w2c_template" +R"w2c_template( return (int)(r); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( + _BitScanForward(&r, (unsigned int)(v >> 32)); +)w2c_template" +R"w2c_template( return (int)(r + 32); +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static inline int I32_CTZ(unsigned long v) { +)w2c_template" +R"w2c_template( if (!v) { +)w2c_template" +R"w2c_template( return 32; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( unsigned long r = 0; +)w2c_template" +R"w2c_template( _BitScanForward(&r, v); +)w2c_template" +R"w2c_template( return (int)r; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +#define POPCOUNT_DEFINE_PORTABLE(f_n, T) \ +)w2c_template" +R"w2c_template( static inline u32 f_n(T x) { \ +)w2c_template" +R"w2c_template( x = x - ((x >> 1) & (T) ~(T)0 / 3); \ +)w2c_template" +R"w2c_template( x = (x & (T) ~(T)0 / 15 * 3) + ((x >> 2) & (T) ~(T)0 / 15 * 3); \ +)w2c_template" +R"w2c_template( x = (x + (x >> 4)) & (T) ~(T)0 / 255 * 15; \ +)w2c_template" +R"w2c_template( return (T)(x * ((T) ~(T)0 / 255)) >> (sizeof(T) - 1) * 8; \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( +POPCOUNT_DEFINE_PORTABLE(I32_POPCNT, u32) +)w2c_template" +R"w2c_template(POPCOUNT_DEFINE_PORTABLE(I64_POPCNT, u64) +)w2c_template" +R"w2c_template( +#undef POPCOUNT_DEFINE_PORTABLE +)w2c_template" +R"w2c_template( +#else +)w2c_template" +R"w2c_template( +#define I32_CLZ(x) ((x) ? __builtin_clz(x) : 32) +)w2c_template" +R"w2c_template(#define I64_CLZ(x) ((x) ? __builtin_clzll(x) : 64) +)w2c_template" +R"w2c_template(#define I32_CTZ(x) ((x) ? __builtin_ctz(x) : 32) +)w2c_template" +R"w2c_template(#define I64_CTZ(x) ((x) ? __builtin_ctzll(x) : 64) +)w2c_template" +R"w2c_template(#define I32_POPCNT(x) (__builtin_popcount(x)) +)w2c_template" +R"w2c_template(#define I64_POPCNT(x) (__builtin_popcountll(x)) +)w2c_template" +R"w2c_template( +#endif +)w2c_template" +R"w2c_template( +#define DIV_S(ut, min, x, y) \ +)w2c_template" +R"w2c_template( ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \ +)w2c_template" +R"w2c_template( : (UNLIKELY((x) == min && (y) == -1)) ? TRAP(INT_OVERFLOW) \ +)w2c_template" +R"w2c_template( : (ut)((x) / (y))) +)w2c_template" +R"w2c_template( +#define REM_S(ut, min, x, y) \ +)w2c_template" +R"w2c_template( ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \ +)w2c_template" +R"w2c_template( : (UNLIKELY((x) == min && (y) == -1)) ? 0 \ +)w2c_template" +R"w2c_template( : (ut)((x) % (y))) +)w2c_template" +R"w2c_template( +#define I32_DIV_S(x, y) DIV_S(u32, INT32_MIN, (s32)x, (s32)y) +)w2c_template" +R"w2c_template(#define I64_DIV_S(x, y) DIV_S(u64, INT64_MIN, (s64)x, (s64)y) +)w2c_template" +R"w2c_template(#define I32_REM_S(x, y) REM_S(u32, INT32_MIN, (s32)x, (s32)y) +)w2c_template" +R"w2c_template(#define I64_REM_S(x, y) REM_S(u64, INT64_MIN, (s64)x, (s64)y) +)w2c_template" +R"w2c_template( +#define DIVREM_U(op, x, y) \ +)w2c_template" +R"w2c_template( ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) : ((x)op(y))) +)w2c_template" +R"w2c_template( +#define DIV_U(x, y) DIVREM_U(/, x, y) +)w2c_template" +R"w2c_template(#define REM_U(x, y) DIVREM_U(%, x, y) +)w2c_template" +R"w2c_template( +#define ROTL(x, y, mask) \ +)w2c_template" +R"w2c_template( (((x) << ((y) & (mask))) | ((x) >> (((mask) - (y) + 1) & (mask)))) +)w2c_template" +R"w2c_template(#define ROTR(x, y, mask) \ +)w2c_template" +R"w2c_template( (((x) >> ((y) & (mask))) | ((x) << (((mask) - (y) + 1) & (mask)))) +)w2c_template" +R"w2c_template( +#define I32_ROTL(x, y) ROTL(x, y, 31) +)w2c_template" +R"w2c_template(#define I64_ROTL(x, y) ROTL(x, y, 63) +)w2c_template" +R"w2c_template(#define I32_ROTR(x, y) ROTR(x, y, 31) +)w2c_template" +R"w2c_template(#define I64_ROTR(x, y) ROTR(x, y, 63) +)w2c_template" +R"w2c_template( +#define FMIN(x, y) \ +)w2c_template" +R"w2c_template( ((UNLIKELY((x) != (x))) ? NAN \ +)w2c_template" +R"w2c_template( : (UNLIKELY((y) != (y))) ? NAN \ +)w2c_template" +R"w2c_template( : (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? x : y) \ +)w2c_template" +R"w2c_template( : (x < y) ? x \ +)w2c_template" +R"w2c_template( : y) +)w2c_template" +R"w2c_template( +#define FMAX(x, y) \ +)w2c_template" +R"w2c_template( ((UNLIKELY((x) != (x))) ? NAN \ +)w2c_template" +R"w2c_template( : (UNLIKELY((y) != (y))) ? NAN \ +)w2c_template" +R"w2c_template( : (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? y : x) \ +)w2c_template" +R"w2c_template( : (x > y) ? x \ +)w2c_template" +R"w2c_template( : y) +)w2c_template" +R"w2c_template( +#define TRUNC_S(ut, st, ft, min, minop, max, x) \ +)w2c_template" +R"w2c_template( ((UNLIKELY((x) != (x))) ? TRAP(INVALID_CONVERSION) \ +)w2c_template" +R"w2c_template( : (UNLIKELY(!((x)minop(min) && (x) < (max)))) ? TRAP(INT_OVERFLOW) \ +)w2c_template" +R"w2c_template( : (ut)(st)(x)) +)w2c_template" +R"w2c_template( +#define I32_TRUNC_S_F32(x) \ +)w2c_template" +R"w2c_template( TRUNC_S(u32, s32, f32, (f32)INT32_MIN, >=, 2147483648.f, x) +)w2c_template" +R"w2c_template(#define I64_TRUNC_S_F32(x) \ +)w2c_template" +R"w2c_template( TRUNC_S(u64, s64, f32, (f32)INT64_MIN, >=, (f32)INT64_MAX, x) +)w2c_template" +R"w2c_template(#define I32_TRUNC_S_F64(x) \ +)w2c_template" +R"w2c_template( TRUNC_S(u32, s32, f64, -2147483649., >, 2147483648., x) +)w2c_template" +R"w2c_template(#define I64_TRUNC_S_F64(x) \ +)w2c_template" +R"w2c_template( TRUNC_S(u64, s64, f64, (f64)INT64_MIN, >=, (f64)INT64_MAX, x) +)w2c_template" +R"w2c_template( +#define TRUNC_U(ut, ft, max, x) \ +)w2c_template" +R"w2c_template( ((UNLIKELY((x) != (x))) ? TRAP(INVALID_CONVERSION) \ +)w2c_template" +R"w2c_template( : (UNLIKELY(!((x) > (ft) - 1 && (x) < (max)))) ? TRAP(INT_OVERFLOW) \ +)w2c_template" +R"w2c_template( : (ut)(x)) +)w2c_template" +R"w2c_template( +#define I32_TRUNC_U_F32(x) TRUNC_U(u32, f32, 4294967296.f, x) +)w2c_template" +R"w2c_template(#define I64_TRUNC_U_F32(x) TRUNC_U(u64, f32, (f32)UINT64_MAX, x) +)w2c_template" +R"w2c_template(#define I32_TRUNC_U_F64(x) TRUNC_U(u32, f64, 4294967296., x) +)w2c_template" +R"w2c_template(#define I64_TRUNC_U_F64(x) TRUNC_U(u64, f64, (f64)UINT64_MAX, x) +)w2c_template" +R"w2c_template( +#define TRUNC_SAT_S(ut, st, ft, min, smin, minop, max, smax, x) \ +)w2c_template" +R"w2c_template( ((UNLIKELY((x) != (x))) ? 0 \ +)w2c_template" +R"w2c_template( : (UNLIKELY(!((x)minop(min)))) ? smin \ +)w2c_template" +R"w2c_template( : (UNLIKELY(!((x) < (max)))) ? smax \ +)w2c_template" +R"w2c_template( : (ut)(st)(x)) +)w2c_template" +R"w2c_template( +#define I32_TRUNC_SAT_S_F32(x) \ +)w2c_template" +R"w2c_template( TRUNC_SAT_S(u32, s32, f32, (f32)INT32_MIN, INT32_MIN, >=, 2147483648.f, \ +)w2c_template" +R"w2c_template( INT32_MAX, x) +)w2c_template" +R"w2c_template(#define I64_TRUNC_SAT_S_F32(x) \ +)w2c_template" +R"w2c_template( TRUNC_SAT_S(u64, s64, f32, (f32)INT64_MIN, INT64_MIN, >=, (f32)INT64_MAX, \ +)w2c_template" +R"w2c_template( INT64_MAX, x) +)w2c_template" +R"w2c_template(#define I32_TRUNC_SAT_S_F64(x) \ +)w2c_template" +R"w2c_template( TRUNC_SAT_S(u32, s32, f64, -2147483649., INT32_MIN, >, 2147483648., \ +)w2c_template" +R"w2c_template( INT32_MAX, x) +)w2c_template" +R"w2c_template(#define I64_TRUNC_SAT_S_F64(x) \ +)w2c_template" +R"w2c_template( TRUNC_SAT_S(u64, s64, f64, (f64)INT64_MIN, INT64_MIN, >=, (f64)INT64_MAX, \ +)w2c_template" +R"w2c_template( INT64_MAX, x) +)w2c_template" +R"w2c_template( +#define TRUNC_SAT_U(ut, ft, max, smax, x) \ +)w2c_template" +R"w2c_template( ((UNLIKELY((x) != (x))) ? 0 \ +)w2c_template" +R"w2c_template( : (UNLIKELY(!((x) > (ft) - 1))) ? 0 \ +)w2c_template" +R"w2c_template( : (UNLIKELY(!((x) < (max)))) ? smax \ +)w2c_template" +R"w2c_template( : (ut)(x)) +)w2c_template" +R"w2c_template( +#define I32_TRUNC_SAT_U_F32(x) \ +)w2c_template" +R"w2c_template( TRUNC_SAT_U(u32, f32, 4294967296.f, UINT32_MAX, x) +)w2c_template" +R"w2c_template(#define I64_TRUNC_SAT_U_F32(x) \ +)w2c_template" +R"w2c_template( TRUNC_SAT_U(u64, f32, (f32)UINT64_MAX, UINT64_MAX, x) +)w2c_template" +R"w2c_template(#define I32_TRUNC_SAT_U_F64(x) TRUNC_SAT_U(u32, f64, 4294967296., UINT32_MAX, x) +)w2c_template" +R"w2c_template(#define I64_TRUNC_SAT_U_F64(x) \ +)w2c_template" +R"w2c_template( TRUNC_SAT_U(u64, f64, (f64)UINT64_MAX, UINT64_MAX, x) +)w2c_template" +R"w2c_template( +#define DEFINE_REINTERPRET(name, t1, t2) \ +)w2c_template" +R"w2c_template( static inline t2 name(t1 x) { \ +)w2c_template" +R"w2c_template( t2 result; \ +)w2c_template" +R"w2c_template( wasm_rt_memcpy(&result, &x, sizeof(result)); \ +)w2c_template" +R"w2c_template( return result; \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( +DEFINE_REINTERPRET(f32_reinterpret_i32, u32, f32) +)w2c_template" +R"w2c_template(DEFINE_REINTERPRET(i32_reinterpret_f32, f32, u32) +)w2c_template" +R"w2c_template(DEFINE_REINTERPRET(f64_reinterpret_i64, u64, f64) +)w2c_template" +R"w2c_template(DEFINE_REINTERPRET(i64_reinterpret_f64, f64, u64) +)w2c_template" +R"w2c_template( +static float quiet_nanf(float x) { +)w2c_template" +R"w2c_template( uint32_t tmp; +)w2c_template" +R"w2c_template( wasm_rt_memcpy(&tmp, &x, 4); +)w2c_template" +R"w2c_template( tmp |= 0x7fc00000lu; +)w2c_template" +R"w2c_template( wasm_rt_memcpy(&x, &tmp, 4); +)w2c_template" +R"w2c_template( return x; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static double quiet_nan(double x) { +)w2c_template" +R"w2c_template( uint64_t tmp; +)w2c_template" +R"w2c_template( wasm_rt_memcpy(&tmp, &x, 8); +)w2c_template" +R"w2c_template( tmp |= 0x7ff8000000000000llu; +)w2c_template" +R"w2c_template( wasm_rt_memcpy(&x, &tmp, 8); +)w2c_template" +R"w2c_template( return x; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static double wasm_quiet(double x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nan(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return x; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static float wasm_quietf(float x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nanf(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return x; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static double wasm_floor(double x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nan(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return floor(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static float wasm_floorf(float x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nanf(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return floorf(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static double wasm_ceil(double x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nan(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return ceil(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static float wasm_ceilf(float x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nanf(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return ceilf(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static double wasm_trunc(double x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nan(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return trunc(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static float wasm_truncf(float x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nanf(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return truncf(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static float wasm_nearbyintf(float x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nanf(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return nearbyintf(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static double wasm_nearbyint(double x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nan(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return nearbyint(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static float wasm_fabsf(float x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( uint32_t tmp; +)w2c_template" +R"w2c_template( wasm_rt_memcpy(&tmp, &x, 4); +)w2c_template" +R"w2c_template( tmp = tmp & ~(1UL << 31); +)w2c_template" +R"w2c_template( wasm_rt_memcpy(&x, &tmp, 4); +)w2c_template" +R"w2c_template( return x; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return fabsf(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static double wasm_fabs(double x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( uint64_t tmp; +)w2c_template" +R"w2c_template( wasm_rt_memcpy(&tmp, &x, 8); +)w2c_template" +R"w2c_template( tmp = tmp & ~(1ULL << 63); +)w2c_template" +R"w2c_template( wasm_rt_memcpy(&x, &tmp, 8); +)w2c_template" +R"w2c_template( return x; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return fabs(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static double wasm_sqrt(double x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nan(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return sqrt(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static float wasm_sqrtf(float x) { +)w2c_template" +R"w2c_template( if (UNLIKELY(isnan(x))) { +)w2c_template" +R"w2c_template( return quiet_nanf(x); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( return sqrtf(x); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static inline void memory_fill(wasm_rt_memory_t* mem, u64 d, u32 val, u64 n) { +)w2c_template" +R"w2c_template( RANGE_CHECK(mem, d, n); +)w2c_template" +R"w2c_template( memset(MEM_ADDR(mem, d, n), val, n); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static inline void memory_copy(wasm_rt_memory_t* dest, +)w2c_template" +R"w2c_template( const wasm_rt_memory_t* src, +)w2c_template" +R"w2c_template( u64 dest_addr, +)w2c_template" +R"w2c_template( u64 src_addr, +)w2c_template" +R"w2c_template( u64 n) { +)w2c_template" +R"w2c_template( RANGE_CHECK(dest, dest_addr, n); +)w2c_template" +R"w2c_template( RANGE_CHECK(src, src_addr, n); +)w2c_template" +R"w2c_template( memmove(MEM_ADDR(dest, dest_addr, n), MEM_ADDR(src, src_addr, n), n); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static inline void memory_init(wasm_rt_memory_t* dest, +)w2c_template" +R"w2c_template( const u8* src, +)w2c_template" +R"w2c_template( u32 src_size, +)w2c_template" +R"w2c_template( u64 dest_addr, +)w2c_template" +R"w2c_template( u32 src_addr, +)w2c_template" +R"w2c_template( u32 n) { +)w2c_template" +R"w2c_template( if (UNLIKELY(src_addr + (uint64_t)n > src_size)) +)w2c_template" +R"w2c_template( TRAP(OOB); +)w2c_template" +R"w2c_template( LOAD_DATA((*dest), dest_addr, src + src_addr, n); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +typedef enum { RefFunc, RefNull, GlobalGet } wasm_elem_segment_expr_type_t; +)w2c_template" +R"w2c_template( +typedef struct { +)w2c_template" +R"w2c_template( wasm_elem_segment_expr_type_t expr_type; +)w2c_template" +R"w2c_template( wasm_rt_func_type_t type; +)w2c_template" +R"w2c_template( wasm_rt_function_ptr_t func; +)w2c_template" +R"w2c_template( wasm_rt_tailcallee_t func_tailcallee; +)w2c_template" +R"w2c_template( size_t module_offset; +)w2c_template" +R"w2c_template(} wasm_elem_segment_expr_t; +)w2c_template" +R"w2c_template( +static inline void funcref_table_init(wasm_rt_funcref_table_t* dest, +)w2c_template" +R"w2c_template( const wasm_elem_segment_expr_t* src, +)w2c_template" +R"w2c_template( u32 src_size, +)w2c_template" +R"w2c_template( u64 dest_addr, +)w2c_template" +R"w2c_template( u32 src_addr, +)w2c_template" +R"w2c_template( u32 n, +)w2c_template" +R"w2c_template( void* module_instance) { +)w2c_template" +R"w2c_template( if (UNLIKELY(src_addr + (uint64_t)n > src_size)) +)w2c_template" +R"w2c_template( TRAP(OOB); +)w2c_template" +R"w2c_template( RANGE_CHECK(dest, dest_addr, n); +)w2c_template" +R"w2c_template( for (u32 i = 0; i < n; i++) { +)w2c_template" +R"w2c_template( const wasm_elem_segment_expr_t* const src_expr = &src[src_addr + i]; +)w2c_template" +R"w2c_template( wasm_rt_funcref_t* const dest_val = &(dest->data[dest_addr + i]); +)w2c_template" +R"w2c_template( switch (src_expr->expr_type) { +)w2c_template" +R"w2c_template( case RefFunc: +)w2c_template" +R"w2c_template( *dest_val = (wasm_rt_funcref_t){ +)w2c_template" +R"w2c_template( src_expr->type, src_expr->func, src_expr->func_tailcallee, +)w2c_template" +R"w2c_template( (char*)module_instance + src_expr->module_offset}; +)w2c_template" +R"w2c_template( break; +)w2c_template" +R"w2c_template( case RefNull: +)w2c_template" +R"w2c_template( *dest_val = wasm_rt_funcref_null_value; +)w2c_template" +R"w2c_template( break; +)w2c_template" +R"w2c_template( case GlobalGet: +)w2c_template" +R"w2c_template( *dest_val = **(wasm_rt_funcref_t**)((char*)module_instance + +)w2c_template" +R"w2c_template( src_expr->module_offset); +)w2c_template" +R"w2c_template( break; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +// Currently wasm2c only supports initializing externref tables with ref.null. +)w2c_template" +R"w2c_template(static inline void externref_table_init(wasm_rt_externref_table_t* dest, +)w2c_template" +R"w2c_template( u32 src_size, +)w2c_template" +R"w2c_template( u64 dest_addr, +)w2c_template" +R"w2c_template( u32 src_addr, +)w2c_template" +R"w2c_template( u32 n) { +)w2c_template" +R"w2c_template( if (UNLIKELY(src_addr + (uint64_t)n > src_size)) +)w2c_template" +R"w2c_template( TRAP(OOB); +)w2c_template" +R"w2c_template( RANGE_CHECK(dest, dest_addr, n); +)w2c_template" +R"w2c_template( for (u32 i = 0; i < n; i++) { +)w2c_template" +R"w2c_template( dest->data[dest_addr + i] = wasm_rt_externref_null_value; +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +#define DEFINE_TABLE_COPY(type) \ +)w2c_template" +R"w2c_template( static inline void type##_table_copy(wasm_rt_##type##_table_t* dest, \ +)w2c_template" +R"w2c_template( const wasm_rt_##type##_table_t* src, \ +)w2c_template" +R"w2c_template( u64 dest_addr, u64 src_addr, u64 n) { \ +)w2c_template" +R"w2c_template( RANGE_CHECK(dest, dest_addr, n); \ +)w2c_template" +R"w2c_template( RANGE_CHECK(src, src_addr, n); \ +)w2c_template" +R"w2c_template( memmove(dest->data + dest_addr, src->data + src_addr, \ +)w2c_template" +R"w2c_template( n * sizeof(wasm_rt_##type##_t)); \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( +DEFINE_TABLE_COPY(funcref) +)w2c_template" +R"w2c_template(DEFINE_TABLE_COPY(externref) +)w2c_template" +R"w2c_template( +#define DEFINE_TABLE_GET(type) \ +)w2c_template" +R"w2c_template( static inline wasm_rt_##type##_t type##_table_get( \ +)w2c_template" +R"w2c_template( const wasm_rt_##type##_table_t* table, u64 i) { \ +)w2c_template" +R"w2c_template( if (UNLIKELY(i >= table->size)) \ +)w2c_template" +R"w2c_template( TRAP(OOB); \ +)w2c_template" +R"w2c_template( return table->data[i]; \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( +DEFINE_TABLE_GET(funcref) +)w2c_template" +R"w2c_template(DEFINE_TABLE_GET(externref) +)w2c_template" +R"w2c_template( +#define DEFINE_TABLE_SET(type) \ +)w2c_template" +R"w2c_template( static inline void type##_table_set(const wasm_rt_##type##_table_t* table, \ +)w2c_template" +R"w2c_template( u64 i, const wasm_rt_##type##_t val) { \ +)w2c_template" +R"w2c_template( if (UNLIKELY(i >= table->size)) \ +)w2c_template" +R"w2c_template( TRAP(OOB); \ +)w2c_template" +R"w2c_template( table->data[i] = val; \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( +DEFINE_TABLE_SET(funcref) +)w2c_template" +R"w2c_template(DEFINE_TABLE_SET(externref) +)w2c_template" +R"w2c_template( +#define DEFINE_TABLE_FILL(type) \ +)w2c_template" +R"w2c_template( static inline void type##_table_fill(const wasm_rt_##type##_table_t* table, \ +)w2c_template" +R"w2c_template( u64 d, const wasm_rt_##type##_t val, \ +)w2c_template" +R"w2c_template( u64 n) { \ +)w2c_template" +R"w2c_template( RANGE_CHECK(table, d, n); \ +)w2c_template" +R"w2c_template( for (uint32_t i = d; i < d + n; i++) { \ +)w2c_template" +R"w2c_template( table->data[i] = val; \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( +DEFINE_TABLE_FILL(funcref) +)w2c_template" +R"w2c_template(DEFINE_TABLE_FILL(externref) +)w2c_template" +R"w2c_template( +#if defined(__GNUC__) || defined(__clang__) +)w2c_template" +R"w2c_template(#define FUNC_TYPE_DECL_EXTERN_T(x) extern const char* const x +)w2c_template" +R"w2c_template(#define FUNC_TYPE_EXTERN_T(x) const char* const x +)w2c_template" +R"w2c_template(#define FUNC_TYPE_T(x) static const char* const x +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#define FUNC_TYPE_DECL_EXTERN_T(x) extern const char x[] +)w2c_template" +R"w2c_template(#define FUNC_TYPE_EXTERN_T(x) const char x[] +)w2c_template" +R"w2c_template(#define FUNC_TYPE_T(x) static const char x[] +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +#if (__STDC_VERSION__ < 201112L) && !defined(static_assert) +)w2c_template" +R"w2c_template(#define static_assert(X) \ +)w2c_template" +R"w2c_template( extern int(*assertion(void))[!!sizeof(struct { int x : (X) ? 2 : -1; })]; +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +#ifdef _MSC_VER +)w2c_template" +R"w2c_template(#define WEAK_FUNC_DECL(func, fallback) \ +)w2c_template" +R"w2c_template( __pragma(comment(linker, "/alternatename:" #func "=" #fallback)) \ +)w2c_template" +R"w2c_template( \ +)w2c_template" +R"w2c_template( void \ +)w2c_template" +R"w2c_template( fallback(void** instance_ptr, void* tail_call_stack, \ +)w2c_template" +R"w2c_template( wasm_rt_tailcallee_t* next) +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#define WEAK_FUNC_DECL(func, fallback) \ +)w2c_template" +R"w2c_template( __attribute__((weak)) void func(void** instance_ptr, void* tail_call_stack, \ +)w2c_template" +R"w2c_template( wasm_rt_tailcallee_t* next) +)w2c_template" +R"w2c_template(#endif +)w2c_template" +; diff --git a/src/tools/wasm2c/prebuilt/wasm2c_source_includes.cpp b/src/tools/wasm2c/prebuilt/wasm2c_source_includes.cpp new file mode 100644 index 00000000000..0ebda772bb5 --- /dev/null +++ b/src/tools/wasm2c/prebuilt/wasm2c_source_includes.cpp @@ -0,0 +1,33 @@ +const char* SourceIncludes = R"w2c_template(#include +)w2c_template" +R"w2c_template(#include +)w2c_template" +R"w2c_template(#include +)w2c_template" +R"w2c_template(#include +)w2c_template" +R"w2c_template(#include +)w2c_template" +R"w2c_template(#if defined(__MINGW32__) +)w2c_template" +R"w2c_template(#include +)w2c_template" +R"w2c_template(#elif defined(_MSC_VER) +)w2c_template" +R"w2c_template(#include +)w2c_template" +R"w2c_template(#include +)w2c_template" +R"w2c_template(#define alloca _alloca +)w2c_template" +R"w2c_template(#elif defined(__FreeBSD__) || defined(__OpenBSD__) +)w2c_template" +R"w2c_template(#include +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#include +)w2c_template" +R"w2c_template(#endif +)w2c_template" +; diff --git a/src/tools/wasm2c/prebuilt/wasm2c_spec_top.cpp b/src/tools/wasm2c/prebuilt/wasm2c_spec_top.cpp new file mode 100644 index 00000000000..bcde7dc9038 --- /dev/null +++ b/src/tools/wasm2c/prebuilt/wasm2c_spec_top.cpp @@ -0,0 +1,945 @@ +const char* SpecTop = R"w2c_template(#include +)w2c_template" +R"w2c_template(#define __STDC_FORMAT_MACROS +)w2c_template" +R"w2c_template(#include +)w2c_template" +R"w2c_template(#include +)w2c_template" +R"w2c_template(#include +)w2c_template" +R"w2c_template(#include +)w2c_template" +R"w2c_template(#include +)w2c_template" +R"w2c_template(#include +)w2c_template" +R"w2c_template(#include +)w2c_template" +R"w2c_template(#include +)w2c_template" +R"w2c_template( +#include "wasm-rt.h" +)w2c_template" +R"w2c_template(#include "wasm-rt-impl.h" +)w2c_template" +R"w2c_template(#include "wasm-rt-exceptions.h" +)w2c_template" +R"w2c_template( +/* NOTE: function argument evaluation order is implementation-defined in C, +)w2c_template" +R"w2c_template( so it SHOULD NOT be relied on by tests. */ +)w2c_template" +R"w2c_template(#if WASM_RT_BIG_ENDIAN +)w2c_template" +R"w2c_template(#define v128_i8x16_make(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \ +)w2c_template" +R"w2c_template( simde_wasm_i8x16_make(p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a) +)w2c_template" +R"w2c_template(#define v128_u8x16_make(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \ +)w2c_template" +R"w2c_template( simde_wasm_u8x16_make(p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a) +)w2c_template" +R"w2c_template(#define v128_i16x8_make(a,b,c,d,e,f,g,h) simde_wasm_i16x8_make(h,g,f,e,d,c,b,a) +)w2c_template" +R"w2c_template(#define v128_u16x8_make(a,b,c,d,e,f,g,h) simde_wasm_u16x8_make(h,g,f,e,d,c,b,a) +)w2c_template" +R"w2c_template(#define v128_i32x4_make(a,b,c,d) simde_wasm_i32x4_make(d,c,b,a) +)w2c_template" +R"w2c_template(#define v128_u32x4_make(a,b,c,d) simde_wasm_u32x4_make(d,c,b,a) +)w2c_template" +R"w2c_template(#define v128_i64x2_make(a,b) simde_wasm_i64x2_make(b,a) +)w2c_template" +R"w2c_template(#define v128_u64x2_make(a,b) simde_wasm_u64x2_make(b,a) +)w2c_template" +R"w2c_template(#define v128_f32x4_make(a,b,c,d) simde_wasm_f32x4_make(d,c,b,a) +)w2c_template" +R"w2c_template(#define v128_f64x2_make(a,b) simde_wasm_f64x2_make(b,a) +)w2c_template" +R"w2c_template(#define v128_i8x16_extract_lane(a,n) simde_wasm_u8x16_extract_lane(a,15-(n)) +)w2c_template" +R"w2c_template(#define v128_u8x16_extract_lane(a,n) simde_wasm_u8x16_extract_lane(a,15-(n)) +)w2c_template" +R"w2c_template(#define v128_i16x8_extract_lane(a,n) simde_wasm_u16x8_extract_lane(a,7-(n)) +)w2c_template" +R"w2c_template(#define v128_u16x8_extract_lane(a,n) simde_wasm_u16x8_extract_lane(a,7-(n)) +)w2c_template" +R"w2c_template(#define v128_i32x4_extract_lane(a,n) simde_wasm_u32x4_extract_lane(a,3-(n)) +)w2c_template" +R"w2c_template(#define v128_u32x4_extract_lane(a,n) simde_wasm_u32x4_extract_lane(a,3-(n)) +)w2c_template" +R"w2c_template(#define v128_i64x2_extract_lane(a,n) simde_wasm_u64x2_extract_lane(a,1-(n)) +)w2c_template" +R"w2c_template(#define v128_u64x2_extract_lane(a,n) simde_wasm_u64x2_extract_lane(a,1-(n)) +)w2c_template" +R"w2c_template(#define v128_f32x4_extract_lane(a,n) simde_wasm_f32x4_extract_lane(a,3-(n)) +)w2c_template" +R"w2c_template(#define v128_f64x2_extract_lane(a,n) simde_wasm_f64x2_extract_lane(a,1-(n)) +)w2c_template" +R"w2c_template(#else +)w2c_template" +R"w2c_template(#define v128_i8x16_make simde_wasm_i8x16_make +)w2c_template" +R"w2c_template(#define v128_u8x16_make simde_wasm_u8x16_make +)w2c_template" +R"w2c_template(#define v128_i16x8_make simde_wasm_i16x8_make +)w2c_template" +R"w2c_template(#define v128_u16x8_make simde_wasm_u16x8_make +)w2c_template" +R"w2c_template(#define v128_i32x4_make simde_wasm_i32x4_make +)w2c_template" +R"w2c_template(#define v128_u32x4_make simde_wasm_u32x4_make +)w2c_template" +R"w2c_template(#define v128_i64x2_make simde_wasm_i64x2_make +)w2c_template" +R"w2c_template(#define v128_u64x2_make simde_wasm_u64x2_make +)w2c_template" +R"w2c_template(#define v128_f32x4_make simde_wasm_f32x4_make +)w2c_template" +R"w2c_template(#define v128_f64x2_make simde_wasm_f64x2_make +)w2c_template" +R"w2c_template(// like is_equal_TYPE below, always use unsigned for these +)w2c_template" +R"w2c_template(#define v128_i8x16_extract_lane simde_wasm_u8x16_extract_lane +)w2c_template" +R"w2c_template(#define v128_u8x16_extract_lane simde_wasm_u8x16_extract_lane +)w2c_template" +R"w2c_template(#define v128_i16x8_extract_lane simde_wasm_u16x8_extract_lane +)w2c_template" +R"w2c_template(#define v128_u16x8_extract_lane simde_wasm_u16x8_extract_lane +)w2c_template" +R"w2c_template(#define v128_i32x4_extract_lane simde_wasm_u32x4_extract_lane +)w2c_template" +R"w2c_template(#define v128_u32x4_extract_lane simde_wasm_u32x4_extract_lane +)w2c_template" +R"w2c_template(#define v128_i64x2_extract_lane simde_wasm_u64x2_extract_lane +)w2c_template" +R"w2c_template(#define v128_u64x2_extract_lane simde_wasm_u64x2_extract_lane +)w2c_template" +R"w2c_template(#define v128_f32x4_extract_lane simde_wasm_f32x4_extract_lane +)w2c_template" +R"w2c_template(#define v128_f64x2_extract_lane simde_wasm_f64x2_extract_lane +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +static int g_tests_run; +)w2c_template" +R"w2c_template(static int g_tests_passed; +)w2c_template" +R"w2c_template( +static void run_spec_tests(void); +)w2c_template" +R"w2c_template( +static void error(const char* file, int line, const char* format, ...) { +)w2c_template" +R"w2c_template( va_list args; +)w2c_template" +R"w2c_template( va_start(args, format); +)w2c_template" +R"w2c_template( fprintf(stderr, "%s:%d: assertion failed: ", file, line); +)w2c_template" +R"w2c_template( vfprintf(stderr, format, args); +)w2c_template" +R"w2c_template( va_end(args); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +#define ASSERT_EXCEPTION(f) \ +)w2c_template" +R"w2c_template( do { \ +)w2c_template" +R"w2c_template( g_tests_run++; \ +)w2c_template" +R"w2c_template( if (wasm_rt_impl_try() == WASM_RT_TRAP_UNCAUGHT_EXCEPTION) { \ +)w2c_template" +R"w2c_template( g_tests_passed++; \ +)w2c_template" +R"w2c_template( } else { \ +)w2c_template" +R"w2c_template( (void)(f); \ +)w2c_template" +R"w2c_template( error(__FILE__, __LINE__, "expected " #f " to throw exception.\n"); \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( } while (0) +)w2c_template" +R"w2c_template( +#define ASSERT_TRAP(f) \ +)w2c_template" +R"w2c_template( do { \ +)w2c_template" +R"w2c_template( g_tests_run++; \ +)w2c_template" +R"w2c_template( if (wasm_rt_impl_try() != 0) { \ +)w2c_template" +R"w2c_template( g_tests_passed++; \ +)w2c_template" +R"w2c_template( } else { \ +)w2c_template" +R"w2c_template( (void)(f); \ +)w2c_template" +R"w2c_template( error(__FILE__, __LINE__, "expected " #f " to trap.\n"); \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( } while (0) +)w2c_template" +R"w2c_template( +#define ASSERT_EXHAUSTION(f) \ +)w2c_template" +R"w2c_template( do { \ +)w2c_template" +R"w2c_template( g_tests_run++; \ +)w2c_template" +R"w2c_template( wasm_rt_trap_t code = wasm_rt_impl_try(); \ +)w2c_template" +R"w2c_template( switch (code) { \ +)w2c_template" +R"w2c_template( case WASM_RT_TRAP_NONE: \ +)w2c_template" +R"w2c_template( (void)(f); \ +)w2c_template" +R"w2c_template( error(__FILE__, __LINE__, "expected " #f " to trap.\n"); \ +)w2c_template" +R"w2c_template( break; \ +)w2c_template" +R"w2c_template( case WASM_RT_TRAP_EXHAUSTION: \ +)w2c_template" +R"w2c_template( g_tests_passed++; \ +)w2c_template" +R"w2c_template( break; \ +)w2c_template" +R"w2c_template( default: \ +)w2c_template" +R"w2c_template( error(__FILE__, __LINE__, \ +)w2c_template" +R"w2c_template( "expected " #f \ +)w2c_template" +R"w2c_template( " to trap due to exhaustion, got trap code %d.\n", \ +)w2c_template" +R"w2c_template( code); \ +)w2c_template" +R"w2c_template( break; \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( } while (0) +)w2c_template" +R"w2c_template( +#define ASSERT_RETURN(f) \ +)w2c_template" +R"w2c_template( do { \ +)w2c_template" +R"w2c_template( g_tests_run++; \ +)w2c_template" +R"w2c_template( int trap_code = wasm_rt_impl_try(); \ +)w2c_template" +R"w2c_template( if (trap_code) { \ +)w2c_template" +R"w2c_template( error(__FILE__, __LINE__, #f " trapped (%s).\n", \ +)w2c_template" +R"w2c_template( wasm_rt_strerror(trap_code)); \ +)w2c_template" +R"w2c_template( } else { \ +)w2c_template" +R"w2c_template( f; \ +)w2c_template" +R"w2c_template( g_tests_passed++; \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( } while (0) +)w2c_template" +R"w2c_template( +#define ASSERT_RETURN_T(type, fmt, f, expected) \ +)w2c_template" +R"w2c_template( do { \ +)w2c_template" +R"w2c_template( g_tests_run++; \ +)w2c_template" +R"w2c_template( int trap_code = wasm_rt_impl_try(); \ +)w2c_template" +R"w2c_template( if (trap_code) { \ +)w2c_template" +R"w2c_template( error(__FILE__, __LINE__, #f " trapped (%s).\n", \ +)w2c_template" +R"w2c_template( wasm_rt_strerror(trap_code)); \ +)w2c_template" +R"w2c_template( } else { \ +)w2c_template" +R"w2c_template( type actual = f; \ +)w2c_template" +R"w2c_template( if (is_equal_##type(actual, expected)) { \ +)w2c_template" +R"w2c_template( g_tests_passed++; \ +)w2c_template" +R"w2c_template( } else { \ +)w2c_template" +R"w2c_template( error(__FILE__, __LINE__, \ +)w2c_template" +R"w2c_template( "in " #f ": expected %" fmt ", got %" fmt ".\n", expected, \ +)w2c_template" +R"w2c_template( actual); \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( } while (0) +)w2c_template" +R"w2c_template( +#define ASSERT_RETURN_FUNCREF(f, expected) \ +)w2c_template" +R"w2c_template( do { \ +)w2c_template" +R"w2c_template( g_tests_run++; \ +)w2c_template" +R"w2c_template( int trap_code = wasm_rt_impl_try(); \ +)w2c_template" +R"w2c_template( if (trap_code) { \ +)w2c_template" +R"w2c_template( error(__FILE__, __LINE__, #f " trapped (%s).\n", \ +)w2c_template" +R"w2c_template( wasm_rt_strerror(trap_code)); \ +)w2c_template" +R"w2c_template( } else { \ +)w2c_template" +R"w2c_template( wasm_rt_funcref_t actual = f; \ +)w2c_template" +R"w2c_template( if (is_equal_wasm_rt_funcref_t(actual, expected)) { \ +)w2c_template" +R"w2c_template( g_tests_passed++; \ +)w2c_template" +R"w2c_template( } else { \ +)w2c_template" +R"w2c_template( error(__FILE__, __LINE__, \ +)w2c_template" +R"w2c_template( "in " #f ": mismatch between expected and actual funcref"); \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( } while (0) +)w2c_template" +R"w2c_template( +#define ASSERT_RETURN_EXNREF(f, expected) \ +)w2c_template" +R"w2c_template( do { \ +)w2c_template" +R"w2c_template( g_tests_run++; \ +)w2c_template" +R"w2c_template( int trap_code = wasm_rt_impl_try(); \ +)w2c_template" +R"w2c_template( if (trap_code) { \ +)w2c_template" +R"w2c_template( error(__FILE__, __LINE__, #f " trapped (%s).\n", \ +)w2c_template" +R"w2c_template( wasm_rt_strerror(trap_code)); \ +)w2c_template" +R"w2c_template( } else { \ +)w2c_template" +R"w2c_template( wasm_rt_exnref_t actual = f; \ +)w2c_template" +R"w2c_template( if (is_equal_wasm_rt_exnref_t(actual, expected)) { \ +)w2c_template" +R"w2c_template( g_tests_passed++; \ +)w2c_template" +R"w2c_template( } else { \ +)w2c_template" +R"w2c_template( error(__FILE__, __LINE__, \ +)w2c_template" +R"w2c_template( "in " #f ": mismatch between expected and actual exnref"); \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( } while (0) +)w2c_template" +R"w2c_template( +#define ASSERT_RETURN_NAN_T(type, itype, fmt, f, kind) \ +)w2c_template" +R"w2c_template( do { \ +)w2c_template" +R"w2c_template( g_tests_run++; \ +)w2c_template" +R"w2c_template( int trap_code = wasm_rt_impl_try(); \ +)w2c_template" +R"w2c_template( if (trap_code) { \ +)w2c_template" +R"w2c_template( error(__FILE__, __LINE__, #f " trapped (%s).\n", \ +)w2c_template" +R"w2c_template( wasm_rt_strerror(trap_code)); \ +)w2c_template" +R"w2c_template( } else { \ +)w2c_template" +R"w2c_template( type actual = f; \ +)w2c_template" +R"w2c_template( itype iactual; \ +)w2c_template" +R"w2c_template( memcpy(&iactual, &actual, sizeof(iactual)); \ +)w2c_template" +R"w2c_template( if (is_##kind##_nan_##type(iactual)) { \ +)w2c_template" +R"w2c_template( g_tests_passed++; \ +)w2c_template" +R"w2c_template( } else { \ +)w2c_template" +R"w2c_template( error(__FILE__, __LINE__, \ +)w2c_template" +R"w2c_template( "in " #f ": expected result to be a " #kind " nan, got 0x%" fmt \ +)w2c_template" +R"w2c_template( ".\n", \ +)w2c_template" +R"w2c_template( iactual); \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( } while (0) +)w2c_template" +R"w2c_template( +#define MULTI_T_UNPACK_(...) __VA_ARGS__ +)w2c_template" +R"w2c_template(#define MULTI_T_UNPACK(arg) MULTI_T_UNPACK_ arg +)w2c_template" +R"w2c_template(#define MULTI_i8 "%" PRIu8 " " +)w2c_template" +R"w2c_template(#define MULTI_i16 "%" PRIu16 " " +)w2c_template" +R"w2c_template(#define MULTI_i32 "%u " +)w2c_template" +R"w2c_template(#define MULTI_i64 "%" PRIu64 " " +)w2c_template" +R"w2c_template(#define MULTI_f32 "%.9g " +)w2c_template" +R"w2c_template(#define MULTI_f64 "%.17g " +)w2c_template" +R"w2c_template(#define MULTI_str "%s " +)w2c_template" +R"w2c_template(#define ASSERT_RETURN_MULTI_T(type, fmt_expected, fmt_got, f, compare, \ +)w2c_template" +R"w2c_template( expected, found) \ +)w2c_template" +R"w2c_template( do { \ +)w2c_template" +R"w2c_template( g_tests_run++; \ +)w2c_template" +R"w2c_template( int trap_code = wasm_rt_impl_try(); \ +)w2c_template" +R"w2c_template( if (trap_code) { \ +)w2c_template" +R"w2c_template( error(__FILE__, __LINE__, #f " trapped (%s).\n", \ +)w2c_template" +R"w2c_template( wasm_rt_strerror(trap_code)); \ +)w2c_template" +R"w2c_template( } else { \ +)w2c_template" +R"w2c_template( type actual = f; \ +)w2c_template" +R"w2c_template( if (compare) { \ +)w2c_template" +R"w2c_template( g_tests_passed++; \ +)w2c_template" +R"w2c_template( } else { \ +)w2c_template" +R"w2c_template( error(__FILE__, __LINE__, \ +)w2c_template" +R"w2c_template( "in " #f ": expected <" fmt_expected ">, got <" fmt_got ">.\n", \ +)w2c_template" +R"w2c_template( MULTI_T_UNPACK(expected), MULTI_T_UNPACK(found)); \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( } \ +)w2c_template" +R"w2c_template( } while (0) +)w2c_template" +R"w2c_template( + +)w2c_template" +R"w2c_template(#define ASSERT_RETURN_I32(f, expected) ASSERT_RETURN_T(u32, "u", f, expected) +)w2c_template" +R"w2c_template(#define ASSERT_RETURN_I64(f, expected) ASSERT_RETURN_T(u64, PRIu64, f, expected) +)w2c_template" +R"w2c_template(#define ASSERT_RETURN_F32(f, expected) ASSERT_RETURN_T(f32, ".9g", f, expected) +)w2c_template" +R"w2c_template(#define ASSERT_RETURN_F64(f, expected) ASSERT_RETURN_T(f64, ".17g", f, expected) +)w2c_template" +R"w2c_template(#define ASSERT_RETURN_EXTERNREF(f, expected) \ +)w2c_template" +R"w2c_template( ASSERT_RETURN_T(wasm_rt_externref_t, "p", f, expected); +)w2c_template" +R"w2c_template( +#define ASSERT_RETURN_CANONICAL_NAN_F32(f) \ +)w2c_template" +R"w2c_template( ASSERT_RETURN_NAN_T(f32, u32, "08x", f, canonical) +)w2c_template" +R"w2c_template(#define ASSERT_RETURN_CANONICAL_NAN_F64(f) \ +)w2c_template" +R"w2c_template( ASSERT_RETURN_NAN_T(f64, u64, "016x", f, canonical) +)w2c_template" +R"w2c_template(#define ASSERT_RETURN_ARITHMETIC_NAN_F32(f) \ +)w2c_template" +R"w2c_template( ASSERT_RETURN_NAN_T(f32, u32, "08x", f, arithmetic) +)w2c_template" +R"w2c_template(#define ASSERT_RETURN_ARITHMETIC_NAN_F64(f) \ +)w2c_template" +R"w2c_template( ASSERT_RETURN_NAN_T(f64, u64, "016x", f, arithmetic) +)w2c_template" +R"w2c_template( +static bool is_equal_u8(u8 x, u8 y) { +)w2c_template" +R"w2c_template( return x == y; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static bool is_equal_u16(u16 x, u16 y) { +)w2c_template" +R"w2c_template( return x == y; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static bool is_equal_u32(u32 x, u32 y) { +)w2c_template" +R"w2c_template( return x == y; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static bool is_equal_u64(u64 x, u64 y) { +)w2c_template" +R"w2c_template( return x == y; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +#define is_equal_i8 is_equal_u8 +)w2c_template" +R"w2c_template(#define is_equal_i16 is_equal_u16 +)w2c_template" +R"w2c_template(#define is_equal_i32 is_equal_u32 +)w2c_template" +R"w2c_template(#define is_equal_i64 is_equal_u64 +)w2c_template" +R"w2c_template( +static bool is_equal_wasm_rt_externref_t(wasm_rt_externref_t x, +)w2c_template" +R"w2c_template( wasm_rt_externref_t y) { +)w2c_template" +R"w2c_template( return x == y; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static inline bool is_equal_wasm_rt_func_type_t(const wasm_rt_func_type_t a, +)w2c_template" +R"w2c_template( const wasm_rt_func_type_t b) { +)w2c_template" +R"w2c_template( return (a == b) || (a && b && !memcmp(a, b, 32)); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static bool is_equal_wasm_rt_funcref_t(wasm_rt_funcref_t x, +)w2c_template" +R"w2c_template( wasm_rt_funcref_t y) { +)w2c_template" +R"w2c_template( return is_equal_wasm_rt_func_type_t(x.func_type, y.func_type) && +)w2c_template" +R"w2c_template( (x.func == y.func) && (x.module_instance == y.module_instance); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +#ifdef WASM_EXN_MAX_SIZE +)w2c_template" +R"w2c_template(static bool is_equal_wasm_rt_exnref_t(wasm_rt_exnref_t x, wasm_rt_exnref_t y) { +)w2c_template" +R"w2c_template( return x.tag == y.tag && x.size == y.size && !memcmp(x.data, y.data, x.size); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +wasm_rt_externref_t spectest_make_externref(uintptr_t x) { +)w2c_template" +R"w2c_template( return (wasm_rt_externref_t)(x + 1); // externref(0) is not null +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static u32 f32_bits(f32 x) { +)w2c_template" +R"w2c_template( u32 ux; +)w2c_template" +R"w2c_template( memcpy(&ux, &x, sizeof(ux)); +)w2c_template" +R"w2c_template( return ux; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static u64 f64_bits(f64 x) { +)w2c_template" +R"w2c_template( u64 ux; +)w2c_template" +R"w2c_template( memcpy(&ux, &x, sizeof(ux)); +)w2c_template" +R"w2c_template( return ux; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static bool is_equal_f32(f32 x, f32 y) { +)w2c_template" +R"w2c_template( return f32_bits(x) == f32_bits(y); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static bool is_equal_f64(f64 x, f64 y) { +)w2c_template" +R"w2c_template( return f64_bits(x) == f64_bits(y); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static f32 make_nan_f32(u32 x) { +)w2c_template" +R"w2c_template( x |= 0x7f800000; +)w2c_template" +R"w2c_template( f32 res; +)w2c_template" +R"w2c_template( memcpy(&res, &x, sizeof(res)); +)w2c_template" +R"w2c_template( return res; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static f64 make_nan_f64(u64 x) { +)w2c_template" +R"w2c_template( x |= 0x7ff0000000000000; +)w2c_template" +R"w2c_template( f64 res; +)w2c_template" +R"w2c_template( memcpy(&res, &x, sizeof(res)); +)w2c_template" +R"w2c_template( return res; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static bool is_canonical_nan_f32(u32 x) { +)w2c_template" +R"w2c_template( return (x & 0x7fffffff) == 0x7fc00000; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static bool is_canonical_nan_f64(u64 x) { +)w2c_template" +R"w2c_template( return (x & 0x7fffffffffffffff) == 0x7ff8000000000000; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static bool is_arithmetic_nan_f32(u32 x) { +)w2c_template" +R"w2c_template( return (x & 0x7fc00000) == 0x7fc00000; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static bool is_arithmetic_nan_f64(u64 x) { +)w2c_template" +R"w2c_template( return (x & 0x7ff8000000000000) == 0x7ff8000000000000; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +typedef struct w2c_spectest { +)w2c_template" +R"w2c_template( wasm_rt_funcref_table_t spectest_table; +)w2c_template" +R"w2c_template( wasm_rt_funcref_table_t spectest_table64; +)w2c_template" +R"w2c_template( wasm_rt_memory_t spectest_memory; +)w2c_template" +R"w2c_template( uint32_t spectest_global_i32; +)w2c_template" +R"w2c_template( uint64_t spectest_global_i64; +)w2c_template" +R"w2c_template( float spectest_global_f32; +)w2c_template" +R"w2c_template( double spectest_global_f64; +)w2c_template" +R"w2c_template(} w2c_spectest; +)w2c_template" +R"w2c_template( +static w2c_spectest spectest_instance; +)w2c_template" +R"w2c_template( +/* +)w2c_template" +R"w2c_template( * spectest implementations +)w2c_template" +R"w2c_template( */ +)w2c_template" +R"w2c_template(void w2c_spectest_print(w2c_spectest* instance) { +)w2c_template" +R"w2c_template( printf("spectest.print()\n"); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +void w2c_spectest_print_i32(w2c_spectest* instance, uint32_t i) { +)w2c_template" +R"w2c_template( printf("spectest.print_i32(%d)\n", i); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +void w2c_spectest_print_i64(w2c_spectest* instance, uint64_t i) { +)w2c_template" +R"w2c_template( printf("spectest.print_i64(%" PRIu64 ")\n", i); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +void w2c_spectest_print_f32(w2c_spectest* instance, float f) { +)w2c_template" +R"w2c_template( printf("spectest.print_f32(%g)\n", f); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +void w2c_spectest_print_i32_f32(w2c_spectest* instance, uint32_t i, float f) { +)w2c_template" +R"w2c_template( printf("spectest.print_i32_f32(%d %g)\n", i, f); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +void w2c_spectest_print_f64(w2c_spectest* instance, double d) { +)w2c_template" +R"w2c_template( printf("spectest.print_f64(%g)\n", d); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +void w2c_spectest_print_f64_f64(w2c_spectest* instance, double d1, double d2) { +)w2c_template" +R"w2c_template( printf("spectest.print_f64_f64(%g %g)\n", d1, d2); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +wasm_rt_funcref_table_t* w2c_spectest_table(w2c_spectest* instance) { +)w2c_template" +R"w2c_template( return &instance->spectest_table; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +wasm_rt_funcref_table_t* w2c_spectest_table64(w2c_spectest* instance) { +)w2c_template" +R"w2c_template( return &instance->spectest_table64; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +wasm_rt_memory_t* w2c_spectest_memory(w2c_spectest* instance) { +)w2c_template" +R"w2c_template( return &instance->spectest_memory; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +uint32_t* w2c_spectest_global_i32(w2c_spectest* instance) { +)w2c_template" +R"w2c_template( return &instance->spectest_global_i32; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +uint64_t* w2c_spectest_global_i64(w2c_spectest* instance) { +)w2c_template" +R"w2c_template( return &instance->spectest_global_i64; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +float* w2c_spectest_global_f32(w2c_spectest* instance) { +)w2c_template" +R"w2c_template( return &instance->spectest_global_f32; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +double* w2c_spectest_global_f64(w2c_spectest* instance) { +)w2c_template" +R"w2c_template( return &instance->spectest_global_f64; +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static void init_spectest_module(w2c_spectest* instance) { +)w2c_template" +R"w2c_template( instance->spectest_global_i32 = 666; +)w2c_template" +R"w2c_template( instance->spectest_global_i64 = 666l; +)w2c_template" +R"w2c_template( instance->spectest_global_f32 = 666.6; +)w2c_template" +R"w2c_template( instance->spectest_global_f64 = 666.6; +)w2c_template" +R"w2c_template( wasm_rt_allocate_memory(&instance->spectest_memory, 1, 2, false, +)w2c_template" +R"w2c_template( WASM_DEFAULT_PAGE_SIZE); +)w2c_template" +R"w2c_template( wasm_rt_allocate_funcref_table(&instance->spectest_table, 10, 20); +)w2c_template" +R"w2c_template( wasm_rt_allocate_funcref_table(&instance->spectest_table64, 10, 20); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +// POSIX-only test config where embedder handles signals instead of w2c runtime +)w2c_template" +R"w2c_template(#ifdef WASM2C_TEST_EMBEDDER_SIGNAL_HANDLING +)w2c_template" +R"w2c_template(#include +)w2c_template" +R"w2c_template( +static void posix_signal_handler(int sig, siginfo_t* si, void* unused) { +)w2c_template" +R"w2c_template( wasm_rt_trap((si->si_code == SEGV_ACCERR) ? WASM_RT_TRAP_OOB +)w2c_template" +R"w2c_template( : WASM_RT_TRAP_EXHAUSTION); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static void posix_install_signal_handler(void) { +)w2c_template" +R"w2c_template( /* install altstack */ +)w2c_template" +R"w2c_template( stack_t ss; +)w2c_template" +R"w2c_template( ss.ss_sp = malloc(SIGSTKSZ); +)w2c_template" +R"w2c_template( ss.ss_flags = 0; +)w2c_template" +R"w2c_template( ss.ss_size = SIGSTKSZ; +)w2c_template" +R"w2c_template( if (sigaltstack(&ss, NULL) != 0) { +)w2c_template" +R"w2c_template( perror("sigaltstack failed"); +)w2c_template" +R"w2c_template( abort(); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( + /* install signal handler */ +)w2c_template" +R"w2c_template( struct sigaction sa; +)w2c_template" +R"w2c_template( memset(&sa, '\0', sizeof(sa)); +)w2c_template" +R"w2c_template( sa.sa_flags = SA_SIGINFO | SA_ONSTACK; +)w2c_template" +R"w2c_template( sigemptyset(&sa.sa_mask); +)w2c_template" +R"w2c_template( sa.sa_sigaction = posix_signal_handler; +)w2c_template" +R"w2c_template( if (sigaction(SIGSEGV, &sa, NULL) != 0 || sigaction(SIGBUS, &sa, NULL) != 0) { +)w2c_template" +R"w2c_template( perror("sigaction failed"); +)w2c_template" +R"w2c_template( abort(); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template( +static void posix_cleanup_signal_handler(void) { +)w2c_template" +R"w2c_template( /* remove signal handler */ +)w2c_template" +R"w2c_template( struct sigaction sa; +)w2c_template" +R"w2c_template( memset(&sa, '\0', sizeof(sa)); +)w2c_template" +R"w2c_template( sa.sa_handler = SIG_DFL; +)w2c_template" +R"w2c_template( if (sigaction(SIGSEGV, &sa, NULL) != 0 || sigaction(SIGBUS, &sa, NULL)) { +)w2c_template" +R"w2c_template( perror("sigaction failed"); +)w2c_template" +R"w2c_template( abort(); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( + /* disable and free altstack */ +)w2c_template" +R"w2c_template( stack_t ss; +)w2c_template" +R"w2c_template( ss.ss_flags = SS_DISABLE; +)w2c_template" +R"w2c_template( if (sigaltstack(&ss, NULL) != 0) { +)w2c_template" +R"w2c_template( perror("sigaltstack failed"); +)w2c_template" +R"w2c_template( abort(); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template( free(ss.ss_sp); +)w2c_template" +R"w2c_template(} +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( +int main(int argc, char** argv) { +)w2c_template" +R"w2c_template(#ifdef WASM2C_TEST_EMBEDDER_SIGNAL_HANDLING +)w2c_template" +R"w2c_template( posix_install_signal_handler(); +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( wasm_rt_init(); +)w2c_template" +R"w2c_template( init_spectest_module(&spectest_instance); +)w2c_template" +R"w2c_template( run_spec_tests(); +)w2c_template" +R"w2c_template( printf("%u/%u tests passed.\n", g_tests_passed, g_tests_run); +)w2c_template" +R"w2c_template( wasm_rt_free(); +)w2c_template" +R"w2c_template(#ifdef WASM2C_TEST_EMBEDDER_SIGNAL_HANDLING +)w2c_template" +R"w2c_template( posix_cleanup_signal_handler(); +)w2c_template" +R"w2c_template(#endif +)w2c_template" +R"w2c_template( return g_tests_passed != g_tests_run; +)w2c_template" +R"w2c_template(} +)w2c_template" +; diff --git a/src/tools/wasm2c/templates/wasm2c.bottom.h b/src/tools/wasm2c/templates/wasm2c.bottom.h new file mode 100644 index 00000000000..d4aa34d6885 --- /dev/null +++ b/src/tools/wasm2c/templates/wasm2c.bottom.h @@ -0,0 +1,3 @@ +#ifdef __cplusplus +} +#endif diff --git a/src/tools/wasm2c/templates/wasm2c.declarations.c b/src/tools/wasm2c/templates/wasm2c.declarations.c new file mode 100644 index 00000000000..26502478a72 --- /dev/null +++ b/src/tools/wasm2c/templates/wasm2c.declarations.c @@ -0,0 +1,725 @@ + +// Computes a pointer to an object of the given size in a little-endian memory. +// +// On a little-endian host, this is just &mem->data[addr] - the object's size is +// unused. On a big-endian host, it's &mem->data[mem->size - addr - n], where n +// is the object's size. +// +// Note that mem may be evaluated multiple times. +// +// Parameters: +// mem - The memory. +// addr - The address. +// n - The size of the object. +// +// Result: +// A pointer for an object of size n. +#if WASM_RT_BIG_ENDIAN +#define MEM_ADDR(mem, addr, n) ((mem)->data_end - (addr) - (n)) +#else +#define MEM_ADDR(mem, addr, n) &((mem)->data[addr]) +#endif + +// We can only use Segue for this module if it uses a single unshared imported +// or exported memory +#if WASM_RT_USE_SEGUE && IS_SINGLE_UNSHARED_MEMORY +#define WASM_RT_USE_SEGUE_FOR_THIS_MODULE 1 +#else +#define WASM_RT_USE_SEGUE_FOR_THIS_MODULE 0 +#endif + +#if WASM_RT_USE_SEGUE_FOR_THIS_MODULE +// POSIX uses FS for TLS, GS is free +static inline void* wasm_rt_segue_read_base() { + if (wasm_rt_fsgsbase_inst_supported) { + return (void*)__builtin_ia32_rdgsbase64(); + } else { + return wasm_rt_syscall_get_segue_base(); + } +} +static inline void wasm_rt_segue_write_base(void* base) { +#if WASM_RT_SEGUE_FREE_SEGMENT + if (wasm_rt_last_segment_val == base) { + return; + } + + wasm_rt_last_segment_val = base; +#endif + + if (wasm_rt_fsgsbase_inst_supported) { + __builtin_ia32_wrgsbase64((uintptr_t)base); + } else { + wasm_rt_syscall_set_segue_base(base); + } +} +#define MEM_ADDR_MEMOP(mem, addr, n) ((uint8_t __seg_gs*)(uintptr_t)addr) +#else +#define MEM_ADDR_MEMOP(mem, addr, n) MEM_ADDR(mem, addr, n) +#endif + +#define TRAP(x) (wasm_rt_trap(WASM_RT_TRAP_##x), 0) + +#if WASM_RT_STACK_DEPTH_COUNT +#define FUNC_PROLOGUE \ + if (++wasm_rt_call_stack_depth > WASM_RT_MAX_CALL_STACK_DEPTH) \ + TRAP(EXHAUSTION); + +#define FUNC_EPILOGUE --wasm_rt_call_stack_depth +#else +#define FUNC_PROLOGUE + +#define FUNC_EPILOGUE +#endif + +#define UNREACHABLE TRAP(UNREACHABLE) + +static inline bool func_types_eq(const wasm_rt_func_type_t a, + const wasm_rt_func_type_t b) { + return (a == b) || LIKELY(a && b && !memcmp(a, b, 32)); +} + +#define CHECK_CALL_INDIRECT(table, ft, x) \ + (LIKELY((x) < table.size && table.data[x].func && \ + func_types_eq(ft, table.data[x].func_type)) || \ + TRAP(CALL_INDIRECT)) + +#define DO_CALL_INDIRECT(table, t, x, ...) ((t)table.data[x].func)(__VA_ARGS__) + +#define CALL_INDIRECT(table, t, ft, x, ...) \ + (CHECK_CALL_INDIRECT(table, ft, x), \ + DO_CALL_INDIRECT(table, t, x, __VA_ARGS__)) + +static inline bool add_overflow(uint64_t a, uint64_t b, uint64_t* resptr) { +#if __has_builtin(__builtin_add_overflow) + return __builtin_add_overflow(a, b, resptr); +#elif defined(_MSC_VER) + return _addcarry_u64(0, a, b, resptr); +#else +#error "Missing implementation of __builtin_add_overflow or _addcarry_u64" +#endif +} + +#define RANGE_CHECK(mem, offset, len) \ + do { \ + uint64_t res; \ + if (UNLIKELY(add_overflow(offset, len, &res))) \ + TRAP(OOB); \ + if (UNLIKELY(res > (mem)->size)) \ + TRAP(OOB); \ + } while (0); + +#if WASM_RT_USE_SEGUE_FOR_THIS_MODULE && WASM_RT_SANITY_CHECKS +#include +#define WASM_RT_CHECK_BASE(mem) \ + if (((uintptr_t)((mem)->data)) != ((uintptr_t)wasm_rt_segue_read_base())) { \ + puts("Segment register mismatch\n"); \ + abort(); \ + } +#else +#define WASM_RT_CHECK_BASE(mem) +#endif + +// MEMCHECK_DEFAULT32 is an "accelerated" MEMCHECK used only for +// default-page-size, 32-bit memories. It may do nothing at all +// (if hardware bounds-checking is enabled via guard pages) +// or it may do a slightly faster RANGE_CHECK. +#if WASM_RT_MEMCHECK_GUARD_PAGES +#define MEMCHECK_DEFAULT32(mem, a, t) WASM_RT_CHECK_BASE(mem); +#else +#define MEMCHECK_DEFAULT32(mem, a, t) \ + WASM_RT_CHECK_BASE(mem); \ + if (UNLIKELY(a + (uint64_t)sizeof(t) > mem->size)) \ + TRAP(OOB); +#endif + +// MEMCHECK_GENERAL can be used for any memory +#define MEMCHECK_GENERAL(mem, a, t) \ + WASM_RT_CHECK_BASE(mem); \ + RANGE_CHECK(mem, a, sizeof(t)); + +#ifdef __GNUC__ +#define FORCE_READ_INT(var) __asm__("" ::"r"(var)); +// Clang on Mips requires "f" constraints on floats +// See https://github.com/llvm/llvm-project/issues/64241 +#if defined(__clang__) && \ + (defined(mips) || defined(__mips__) || defined(__mips)) +#define FORCE_READ_FLOAT(var) __asm__("" ::"f"(var)); +#else +#define FORCE_READ_FLOAT(var) __asm__("" ::"r"(var)); +#endif +#else +#define FORCE_READ_INT(var) +#define FORCE_READ_FLOAT(var) +#endif + +static inline void load_data(u8* dest, const u8* src, size_t n) { + if (!n) { + return; + } +#if WASM_RT_BIG_ENDIAN + for (size_t i = 0; i < n; i++) { + dest[i] = src[n - i - 1]; + } +#else + wasm_rt_memcpy(dest, src, n); +#endif +} + +#define LOAD_DATA(m, o, i, s) \ + do { \ + RANGE_CHECK((&m), o, s); \ + load_data(MEM_ADDR(&m, o, s), i, s); \ + } while (0) + +#define DEF_MEM_CHECKS0(name, shared, mem_type, ret_kw, return_type) \ + static inline return_type name##_default32(wasm_rt##shared##memory_t* mem, \ + u64 addr) { \ + MEMCHECK_DEFAULT32(mem, addr, mem_type); \ + ret_kw name##_unchecked(mem, addr); \ + } \ + static inline return_type name(wasm_rt##shared##memory_t* mem, u64 addr) { \ + MEMCHECK_GENERAL(mem, addr, mem_type); \ + ret_kw name##_unchecked(mem, addr); \ + } + +#define DEF_MEM_CHECKS1(name, shared, mem_type, ret_kw, return_type, \ + val_type1) \ + static inline return_type name##_default32(wasm_rt##shared##memory_t* mem, \ + u64 addr, val_type1 val1) { \ + MEMCHECK_DEFAULT32(mem, addr, mem_type); \ + ret_kw name##_unchecked(mem, addr, val1); \ + } \ + static inline return_type name(wasm_rt##shared##memory_t* mem, u64 addr, \ + val_type1 val1) { \ + MEMCHECK_GENERAL(mem, addr, mem_type); \ + ret_kw name##_unchecked(mem, addr, val1); \ + } + +#define DEF_MEM_CHECKS2(name, shared, mem_type, ret_kw, return_type, \ + val_type1, val_type2) \ + static inline return_type name##_default32(wasm_rt##shared##memory_t* mem, \ + u64 addr, val_type1 val1, \ + val_type2 val2) { \ + MEMCHECK_DEFAULT32(mem, addr, mem_type); \ + ret_kw name##_unchecked(mem, addr, val1, val2); \ + } \ + static inline return_type name(wasm_rt##shared##memory_t* mem, u64 addr, \ + val_type1 val1, val_type2 val2) { \ + MEMCHECK_GENERAL(mem, addr, mem_type); \ + ret_kw name##_unchecked(mem, addr, val1, val2); \ + } + +#define DEFINE_LOAD(name, t1, t2, t3, force_read) \ + static inline t3 name##_unchecked(wasm_rt_memory_t* mem, u64 addr) { \ + t1 result; \ + wasm_rt_memcpy(&result, MEM_ADDR_MEMOP(mem, addr, sizeof(t1)), \ + sizeof(t1)); \ + force_read(result); \ + return (t3)(t2)result; \ + } \ + DEF_MEM_CHECKS0(name, _, t1, return, t3) + +#define DEFINE_STORE(name, t1, t2) \ + static inline void name##_unchecked(wasm_rt_memory_t* mem, u64 addr, \ + t2 value) { \ + t1 wrapped = (t1)value; \ + wasm_rt_memcpy(MEM_ADDR_MEMOP(mem, addr, sizeof(t1)), &wrapped, \ + sizeof(t1)); \ + } \ + DEF_MEM_CHECKS1(name, _, t1, , void, t2) + +DEFINE_LOAD(i32_load, u32, u32, u32, FORCE_READ_INT) +DEFINE_LOAD(i64_load, u64, u64, u64, FORCE_READ_INT) +DEFINE_LOAD(f32_load, f32, f32, f32, FORCE_READ_FLOAT) +DEFINE_LOAD(f64_load, f64, f64, f64, FORCE_READ_FLOAT) +DEFINE_LOAD(i32_load8_s, s8, s32, u32, FORCE_READ_INT) +DEFINE_LOAD(i64_load8_s, s8, s64, u64, FORCE_READ_INT) +DEFINE_LOAD(i32_load8_u, u8, u32, u32, FORCE_READ_INT) +DEFINE_LOAD(i64_load8_u, u8, u64, u64, FORCE_READ_INT) +DEFINE_LOAD(i32_load16_s, s16, s32, u32, FORCE_READ_INT) +DEFINE_LOAD(i64_load16_s, s16, s64, u64, FORCE_READ_INT) +DEFINE_LOAD(i32_load16_u, u16, u32, u32, FORCE_READ_INT) +DEFINE_LOAD(i64_load16_u, u16, u64, u64, FORCE_READ_INT) +DEFINE_LOAD(i64_load32_s, s32, s64, u64, FORCE_READ_INT) +DEFINE_LOAD(i64_load32_u, u32, u64, u64, FORCE_READ_INT) +DEFINE_STORE(i32_store, u32, u32) +DEFINE_STORE(i64_store, u64, u64) +DEFINE_STORE(f32_store, f32, f32) +DEFINE_STORE(f64_store, f64, f64) +DEFINE_STORE(i32_store8, u8, u32) +DEFINE_STORE(i32_store16, u16, u32) +DEFINE_STORE(i64_store8, u8, u64) +DEFINE_STORE(i64_store16, u16, u64) +DEFINE_STORE(i64_store32, u32, u64) + +#if defined(_MSC_VER) + +// Adapted from +// https://github.com/nemequ/portable-snippets/blob/master/builtin/builtin.h + +static inline int I64_CLZ(unsigned long long v) { + unsigned long r = 0; +#if defined(_M_AMD64) || defined(_M_ARM) + if (_BitScanReverse64(&r, v)) { + return 63 - r; + } +#else + if (_BitScanReverse(&r, (unsigned long)(v >> 32))) { + return 31 - r; + } else if (_BitScanReverse(&r, (unsigned long)v)) { + return 63 - r; + } +#endif + return 64; +} + +static inline int I32_CLZ(unsigned long v) { + unsigned long r = 0; + if (_BitScanReverse(&r, v)) { + return 31 - r; + } + return 32; +} + +static inline int I64_CTZ(unsigned long long v) { + if (!v) { + return 64; + } + unsigned long r = 0; +#if defined(_M_AMD64) || defined(_M_ARM) + _BitScanForward64(&r, v); + return (int)r; +#else + if (_BitScanForward(&r, (unsigned int)(v))) { + return (int)(r); + } + + _BitScanForward(&r, (unsigned int)(v >> 32)); + return (int)(r + 32); +#endif +} + +static inline int I32_CTZ(unsigned long v) { + if (!v) { + return 32; + } + unsigned long r = 0; + _BitScanForward(&r, v); + return (int)r; +} + +#define POPCOUNT_DEFINE_PORTABLE(f_n, T) \ + static inline u32 f_n(T x) { \ + x = x - ((x >> 1) & (T) ~(T)0 / 3); \ + x = (x & (T) ~(T)0 / 15 * 3) + ((x >> 2) & (T) ~(T)0 / 15 * 3); \ + x = (x + (x >> 4)) & (T) ~(T)0 / 255 * 15; \ + return (T)(x * ((T) ~(T)0 / 255)) >> (sizeof(T) - 1) * 8; \ + } + +POPCOUNT_DEFINE_PORTABLE(I32_POPCNT, u32) +POPCOUNT_DEFINE_PORTABLE(I64_POPCNT, u64) + +#undef POPCOUNT_DEFINE_PORTABLE + +#else + +#define I32_CLZ(x) ((x) ? __builtin_clz(x) : 32) +#define I64_CLZ(x) ((x) ? __builtin_clzll(x) : 64) +#define I32_CTZ(x) ((x) ? __builtin_ctz(x) : 32) +#define I64_CTZ(x) ((x) ? __builtin_ctzll(x) : 64) +#define I32_POPCNT(x) (__builtin_popcount(x)) +#define I64_POPCNT(x) (__builtin_popcountll(x)) + +#endif + +#define DIV_S(ut, min, x, y) \ + ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \ + : (UNLIKELY((x) == min && (y) == -1)) ? TRAP(INT_OVERFLOW) \ + : (ut)((x) / (y))) + +#define REM_S(ut, min, x, y) \ + ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \ + : (UNLIKELY((x) == min && (y) == -1)) ? 0 \ + : (ut)((x) % (y))) + +#define I32_DIV_S(x, y) DIV_S(u32, INT32_MIN, (s32)x, (s32)y) +#define I64_DIV_S(x, y) DIV_S(u64, INT64_MIN, (s64)x, (s64)y) +#define I32_REM_S(x, y) REM_S(u32, INT32_MIN, (s32)x, (s32)y) +#define I64_REM_S(x, y) REM_S(u64, INT64_MIN, (s64)x, (s64)y) + +#define DIVREM_U(op, x, y) \ + ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) : ((x)op(y))) + +#define DIV_U(x, y) DIVREM_U(/, x, y) +#define REM_U(x, y) DIVREM_U(%, x, y) + +#define ROTL(x, y, mask) \ + (((x) << ((y) & (mask))) | ((x) >> (((mask) - (y) + 1) & (mask)))) +#define ROTR(x, y, mask) \ + (((x) >> ((y) & (mask))) | ((x) << (((mask) - (y) + 1) & (mask)))) + +#define I32_ROTL(x, y) ROTL(x, y, 31) +#define I64_ROTL(x, y) ROTL(x, y, 63) +#define I32_ROTR(x, y) ROTR(x, y, 31) +#define I64_ROTR(x, y) ROTR(x, y, 63) + +#define FMIN(x, y) \ + ((UNLIKELY((x) != (x))) ? NAN \ + : (UNLIKELY((y) != (y))) ? NAN \ + : (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? x : y) \ + : (x < y) ? x \ + : y) + +#define FMAX(x, y) \ + ((UNLIKELY((x) != (x))) ? NAN \ + : (UNLIKELY((y) != (y))) ? NAN \ + : (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? y : x) \ + : (x > y) ? x \ + : y) + +#define TRUNC_S(ut, st, ft, min, minop, max, x) \ + ((UNLIKELY((x) != (x))) ? TRAP(INVALID_CONVERSION) \ + : (UNLIKELY(!((x)minop(min) && (x) < (max)))) ? TRAP(INT_OVERFLOW) \ + : (ut)(st)(x)) + +#define I32_TRUNC_S_F32(x) \ + TRUNC_S(u32, s32, f32, (f32)INT32_MIN, >=, 2147483648.f, x) +#define I64_TRUNC_S_F32(x) \ + TRUNC_S(u64, s64, f32, (f32)INT64_MIN, >=, (f32)INT64_MAX, x) +#define I32_TRUNC_S_F64(x) \ + TRUNC_S(u32, s32, f64, -2147483649., >, 2147483648., x) +#define I64_TRUNC_S_F64(x) \ + TRUNC_S(u64, s64, f64, (f64)INT64_MIN, >=, (f64)INT64_MAX, x) + +#define TRUNC_U(ut, ft, max, x) \ + ((UNLIKELY((x) != (x))) ? TRAP(INVALID_CONVERSION) \ + : (UNLIKELY(!((x) > (ft) - 1 && (x) < (max)))) ? TRAP(INT_OVERFLOW) \ + : (ut)(x)) + +#define I32_TRUNC_U_F32(x) TRUNC_U(u32, f32, 4294967296.f, x) +#define I64_TRUNC_U_F32(x) TRUNC_U(u64, f32, (f32)UINT64_MAX, x) +#define I32_TRUNC_U_F64(x) TRUNC_U(u32, f64, 4294967296., x) +#define I64_TRUNC_U_F64(x) TRUNC_U(u64, f64, (f64)UINT64_MAX, x) + +#define TRUNC_SAT_S(ut, st, ft, min, smin, minop, max, smax, x) \ + ((UNLIKELY((x) != (x))) ? 0 \ + : (UNLIKELY(!((x)minop(min)))) ? smin \ + : (UNLIKELY(!((x) < (max)))) ? smax \ + : (ut)(st)(x)) + +#define I32_TRUNC_SAT_S_F32(x) \ + TRUNC_SAT_S(u32, s32, f32, (f32)INT32_MIN, INT32_MIN, >=, 2147483648.f, \ + INT32_MAX, x) +#define I64_TRUNC_SAT_S_F32(x) \ + TRUNC_SAT_S(u64, s64, f32, (f32)INT64_MIN, INT64_MIN, >=, (f32)INT64_MAX, \ + INT64_MAX, x) +#define I32_TRUNC_SAT_S_F64(x) \ + TRUNC_SAT_S(u32, s32, f64, -2147483649., INT32_MIN, >, 2147483648., \ + INT32_MAX, x) +#define I64_TRUNC_SAT_S_F64(x) \ + TRUNC_SAT_S(u64, s64, f64, (f64)INT64_MIN, INT64_MIN, >=, (f64)INT64_MAX, \ + INT64_MAX, x) + +#define TRUNC_SAT_U(ut, ft, max, smax, x) \ + ((UNLIKELY((x) != (x))) ? 0 \ + : (UNLIKELY(!((x) > (ft) - 1))) ? 0 \ + : (UNLIKELY(!((x) < (max)))) ? smax \ + : (ut)(x)) + +#define I32_TRUNC_SAT_U_F32(x) \ + TRUNC_SAT_U(u32, f32, 4294967296.f, UINT32_MAX, x) +#define I64_TRUNC_SAT_U_F32(x) \ + TRUNC_SAT_U(u64, f32, (f32)UINT64_MAX, UINT64_MAX, x) +#define I32_TRUNC_SAT_U_F64(x) TRUNC_SAT_U(u32, f64, 4294967296., UINT32_MAX, x) +#define I64_TRUNC_SAT_U_F64(x) \ + TRUNC_SAT_U(u64, f64, (f64)UINT64_MAX, UINT64_MAX, x) + +#define DEFINE_REINTERPRET(name, t1, t2) \ + static inline t2 name(t1 x) { \ + t2 result; \ + wasm_rt_memcpy(&result, &x, sizeof(result)); \ + return result; \ + } + +DEFINE_REINTERPRET(f32_reinterpret_i32, u32, f32) +DEFINE_REINTERPRET(i32_reinterpret_f32, f32, u32) +DEFINE_REINTERPRET(f64_reinterpret_i64, u64, f64) +DEFINE_REINTERPRET(i64_reinterpret_f64, f64, u64) + +static float quiet_nanf(float x) { + uint32_t tmp; + wasm_rt_memcpy(&tmp, &x, 4); + tmp |= 0x7fc00000lu; + wasm_rt_memcpy(&x, &tmp, 4); + return x; +} + +static double quiet_nan(double x) { + uint64_t tmp; + wasm_rt_memcpy(&tmp, &x, 8); + tmp |= 0x7ff8000000000000llu; + wasm_rt_memcpy(&x, &tmp, 8); + return x; +} + +static double wasm_quiet(double x) { + if (UNLIKELY(isnan(x))) { + return quiet_nan(x); + } + return x; +} + +static float wasm_quietf(float x) { + if (UNLIKELY(isnan(x))) { + return quiet_nanf(x); + } + return x; +} + +static double wasm_floor(double x) { + if (UNLIKELY(isnan(x))) { + return quiet_nan(x); + } + return floor(x); +} + +static float wasm_floorf(float x) { + if (UNLIKELY(isnan(x))) { + return quiet_nanf(x); + } + return floorf(x); +} + +static double wasm_ceil(double x) { + if (UNLIKELY(isnan(x))) { + return quiet_nan(x); + } + return ceil(x); +} + +static float wasm_ceilf(float x) { + if (UNLIKELY(isnan(x))) { + return quiet_nanf(x); + } + return ceilf(x); +} + +static double wasm_trunc(double x) { + if (UNLIKELY(isnan(x))) { + return quiet_nan(x); + } + return trunc(x); +} + +static float wasm_truncf(float x) { + if (UNLIKELY(isnan(x))) { + return quiet_nanf(x); + } + return truncf(x); +} + +static float wasm_nearbyintf(float x) { + if (UNLIKELY(isnan(x))) { + return quiet_nanf(x); + } + return nearbyintf(x); +} + +static double wasm_nearbyint(double x) { + if (UNLIKELY(isnan(x))) { + return quiet_nan(x); + } + return nearbyint(x); +} + +static float wasm_fabsf(float x) { + if (UNLIKELY(isnan(x))) { + uint32_t tmp; + wasm_rt_memcpy(&tmp, &x, 4); + tmp = tmp & ~(1UL << 31); + wasm_rt_memcpy(&x, &tmp, 4); + return x; + } + return fabsf(x); +} + +static double wasm_fabs(double x) { + if (UNLIKELY(isnan(x))) { + uint64_t tmp; + wasm_rt_memcpy(&tmp, &x, 8); + tmp = tmp & ~(1ULL << 63); + wasm_rt_memcpy(&x, &tmp, 8); + return x; + } + return fabs(x); +} + +static double wasm_sqrt(double x) { + if (UNLIKELY(isnan(x))) { + return quiet_nan(x); + } + return sqrt(x); +} + +static float wasm_sqrtf(float x) { + if (UNLIKELY(isnan(x))) { + return quiet_nanf(x); + } + return sqrtf(x); +} + +static inline void memory_fill(wasm_rt_memory_t* mem, u64 d, u32 val, u64 n) { + RANGE_CHECK(mem, d, n); + memset(MEM_ADDR(mem, d, n), val, n); +} + +static inline void memory_copy(wasm_rt_memory_t* dest, + const wasm_rt_memory_t* src, + u64 dest_addr, + u64 src_addr, + u64 n) { + RANGE_CHECK(dest, dest_addr, n); + RANGE_CHECK(src, src_addr, n); + memmove(MEM_ADDR(dest, dest_addr, n), MEM_ADDR(src, src_addr, n), n); +} + +static inline void memory_init(wasm_rt_memory_t* dest, + const u8* src, + u32 src_size, + u64 dest_addr, + u32 src_addr, + u32 n) { + if (UNLIKELY(src_addr + (uint64_t)n > src_size)) + TRAP(OOB); + LOAD_DATA((*dest), dest_addr, src + src_addr, n); +} + +typedef enum { RefFunc, RefNull, GlobalGet } wasm_elem_segment_expr_type_t; + +typedef struct { + wasm_elem_segment_expr_type_t expr_type; + wasm_rt_func_type_t type; + wasm_rt_function_ptr_t func; + wasm_rt_tailcallee_t func_tailcallee; + size_t module_offset; +} wasm_elem_segment_expr_t; + +static inline void funcref_table_init(wasm_rt_funcref_table_t* dest, + const wasm_elem_segment_expr_t* src, + u32 src_size, + u64 dest_addr, + u32 src_addr, + u32 n, + void* module_instance) { + if (UNLIKELY(src_addr + (uint64_t)n > src_size)) + TRAP(OOB); + RANGE_CHECK(dest, dest_addr, n); + for (u32 i = 0; i < n; i++) { + const wasm_elem_segment_expr_t* const src_expr = &src[src_addr + i]; + wasm_rt_funcref_t* const dest_val = &(dest->data[dest_addr + i]); + switch (src_expr->expr_type) { + case RefFunc: + *dest_val = (wasm_rt_funcref_t){ + src_expr->type, src_expr->func, src_expr->func_tailcallee, + (char*)module_instance + src_expr->module_offset}; + break; + case RefNull: + *dest_val = wasm_rt_funcref_null_value; + break; + case GlobalGet: + *dest_val = **(wasm_rt_funcref_t**)((char*)module_instance + + src_expr->module_offset); + break; + } + } +} + +// Currently wasm2c only supports initializing externref tables with ref.null. +static inline void externref_table_init(wasm_rt_externref_table_t* dest, + u32 src_size, + u64 dest_addr, + u32 src_addr, + u32 n) { + if (UNLIKELY(src_addr + (uint64_t)n > src_size)) + TRAP(OOB); + RANGE_CHECK(dest, dest_addr, n); + for (u32 i = 0; i < n; i++) { + dest->data[dest_addr + i] = wasm_rt_externref_null_value; + } +} + +#define DEFINE_TABLE_COPY(type) \ + static inline void type##_table_copy(wasm_rt_##type##_table_t* dest, \ + const wasm_rt_##type##_table_t* src, \ + u64 dest_addr, u64 src_addr, u64 n) { \ + RANGE_CHECK(dest, dest_addr, n); \ + RANGE_CHECK(src, src_addr, n); \ + memmove(dest->data + dest_addr, src->data + src_addr, \ + n * sizeof(wasm_rt_##type##_t)); \ + } + +DEFINE_TABLE_COPY(funcref) +DEFINE_TABLE_COPY(externref) + +#define DEFINE_TABLE_GET(type) \ + static inline wasm_rt_##type##_t type##_table_get( \ + const wasm_rt_##type##_table_t* table, u64 i) { \ + if (UNLIKELY(i >= table->size)) \ + TRAP(OOB); \ + return table->data[i]; \ + } + +DEFINE_TABLE_GET(funcref) +DEFINE_TABLE_GET(externref) + +#define DEFINE_TABLE_SET(type) \ + static inline void type##_table_set(const wasm_rt_##type##_table_t* table, \ + u64 i, const wasm_rt_##type##_t val) { \ + if (UNLIKELY(i >= table->size)) \ + TRAP(OOB); \ + table->data[i] = val; \ + } + +DEFINE_TABLE_SET(funcref) +DEFINE_TABLE_SET(externref) + +#define DEFINE_TABLE_FILL(type) \ + static inline void type##_table_fill(const wasm_rt_##type##_table_t* table, \ + u64 d, const wasm_rt_##type##_t val, \ + u64 n) { \ + RANGE_CHECK(table, d, n); \ + for (uint32_t i = d; i < d + n; i++) { \ + table->data[i] = val; \ + } \ + } + +DEFINE_TABLE_FILL(funcref) +DEFINE_TABLE_FILL(externref) + +#if defined(__GNUC__) || defined(__clang__) +#define FUNC_TYPE_DECL_EXTERN_T(x) extern const char* const x +#define FUNC_TYPE_EXTERN_T(x) const char* const x +#define FUNC_TYPE_T(x) static const char* const x +#else +#define FUNC_TYPE_DECL_EXTERN_T(x) extern const char x[] +#define FUNC_TYPE_EXTERN_T(x) const char x[] +#define FUNC_TYPE_T(x) static const char x[] +#endif + +#if (__STDC_VERSION__ < 201112L) && !defined(static_assert) +#define static_assert(X) \ + extern int(*assertion(void))[!!sizeof(struct { int x : (X) ? 2 : -1; })]; +#endif + +#ifdef _MSC_VER +#define WEAK_FUNC_DECL(func, fallback) \ + __pragma(comment(linker, "/alternatename:" #func "=" #fallback)) \ + \ + void \ + fallback(void** instance_ptr, void* tail_call_stack, \ + wasm_rt_tailcallee_t* next) +#else +#define WEAK_FUNC_DECL(func, fallback) \ + __attribute__((weak)) void func(void** instance_ptr, void* tail_call_stack, \ + wasm_rt_tailcallee_t* next) +#endif diff --git a/src/tools/wasm2c/templates/wasm2c.includes.c b/src/tools/wasm2c/templates/wasm2c.includes.c new file mode 100644 index 00000000000..bc5a1c96269 --- /dev/null +++ b/src/tools/wasm2c/templates/wasm2c.includes.c @@ -0,0 +1,16 @@ +#include +#include +#include +#include +#include +#if defined(__MINGW32__) +#include +#elif defined(_MSC_VER) +#include +#include +#define alloca _alloca +#elif defined(__FreeBSD__) || defined(__OpenBSD__) +#include +#else +#include +#endif diff --git a/src/tools/wasm2c/templates/wasm2c.top.h b/src/tools/wasm2c/templates/wasm2c.top.h new file mode 100644 index 00000000000..e14197ab669 --- /dev/null +++ b/src/tools/wasm2c/templates/wasm2c.top.h @@ -0,0 +1,19 @@ +#include + +#ifndef WASM_RT_CORE_TYPES_DEFINED +#define WASM_RT_CORE_TYPES_DEFINED +typedef uint8_t u8; +typedef int8_t s8; +typedef uint16_t u16; +typedef int16_t s16; +typedef uint32_t u32; +typedef int32_t s32; +typedef uint64_t u64; +typedef int64_t s64; +typedef float f32; +typedef double f64; +#endif + +#ifdef __cplusplus +extern "C" { +#endif diff --git a/src/tools/wasm2c/templates/wasm2c_atomicops.declarations.c b/src/tools/wasm2c/templates/wasm2c_atomicops.declarations.c new file mode 100644 index 00000000000..cb25da5135b --- /dev/null +++ b/src/tools/wasm2c/templates/wasm2c_atomicops.declarations.c @@ -0,0 +1,237 @@ +#include + +#ifndef WASM_RT_C11_AVAILABLE +#error "C11 is required for Wasm threads and shared memory support" +#endif + +#define ATOMIC_ALIGNMENT_CHECK(addr, t1) \ + if (UNLIKELY(addr % sizeof(t1))) { \ + TRAP(UNALIGNED); \ + } + +#define DEFINE_SHARED_LOAD(name, t1, t2, t3, force_read) \ + static inline t3 name##_unchecked(wasm_rt_shared_memory_t* mem, u64 addr) { \ + t1 result; \ + result = atomic_load_explicit( \ + (_Atomic volatile t1*)MEM_ADDR(mem, addr, sizeof(t1)), \ + memory_order_relaxed); \ + force_read(result); \ + return (t3)(t2)result; \ + } \ + DEF_MEM_CHECKS0(name, _shared_, t1, return, t3) + +DEFINE_SHARED_LOAD(i32_load_shared, u32, u32, u32, FORCE_READ_INT) +DEFINE_SHARED_LOAD(i64_load_shared, u64, u64, u64, FORCE_READ_INT) +DEFINE_SHARED_LOAD(f32_load_shared, f32, f32, f32, FORCE_READ_FLOAT) +DEFINE_SHARED_LOAD(f64_load_shared, f64, f64, f64, FORCE_READ_FLOAT) +DEFINE_SHARED_LOAD(i32_load8_s_shared, s8, s32, u32, FORCE_READ_INT) +DEFINE_SHARED_LOAD(i64_load8_s_shared, s8, s64, u64, FORCE_READ_INT) +DEFINE_SHARED_LOAD(i32_load8_u_shared, u8, u32, u32, FORCE_READ_INT) +DEFINE_SHARED_LOAD(i64_load8_u_shared, u8, u64, u64, FORCE_READ_INT) +DEFINE_SHARED_LOAD(i32_load16_s_shared, s16, s32, u32, FORCE_READ_INT) +DEFINE_SHARED_LOAD(i64_load16_s_shared, s16, s64, u64, FORCE_READ_INT) +DEFINE_SHARED_LOAD(i32_load16_u_shared, u16, u32, u32, FORCE_READ_INT) +DEFINE_SHARED_LOAD(i64_load16_u_shared, u16, u64, u64, FORCE_READ_INT) +DEFINE_SHARED_LOAD(i64_load32_s_shared, s32, s64, u64, FORCE_READ_INT) +DEFINE_SHARED_LOAD(i64_load32_u_shared, u32, u64, u64, FORCE_READ_INT) + +#define DEFINE_SHARED_STORE(name, t1, t2) \ + static inline void name##_unchecked(wasm_rt_shared_memory_t* mem, u64 addr, \ + t2 value) { \ + t1 wrapped = (t1)value; \ + atomic_store_explicit( \ + (_Atomic volatile t1*)MEM_ADDR(mem, addr, sizeof(t1)), wrapped, \ + memory_order_relaxed); \ + } \ + DEF_MEM_CHECKS1(name, _shared_, t1, , void, t2) + +DEFINE_SHARED_STORE(i32_store_shared, u32, u32) +DEFINE_SHARED_STORE(i64_store_shared, u64, u64) +DEFINE_SHARED_STORE(f32_store_shared, f32, f32) +DEFINE_SHARED_STORE(f64_store_shared, f64, f64) +DEFINE_SHARED_STORE(i32_store8_shared, u8, u32) +DEFINE_SHARED_STORE(i32_store16_shared, u16, u32) +DEFINE_SHARED_STORE(i64_store8_shared, u8, u64) +DEFINE_SHARED_STORE(i64_store16_shared, u16, u64) +DEFINE_SHARED_STORE(i64_store32_shared, u32, u64) + +#define DEFINE_ATOMIC_LOAD(name, t1, t2, t3, force_read) \ + static inline t3 name##_unchecked(wasm_rt_memory_t* mem, u64 addr) { \ + ATOMIC_ALIGNMENT_CHECK(addr, t1); \ + t1 result; \ + wasm_rt_memcpy(&result, MEM_ADDR(mem, addr, sizeof(t1)), sizeof(t1)); \ + force_read(result); \ + return (t3)(t2)result; \ + } \ + DEF_MEM_CHECKS0(name, _, t1, return, t3) \ + static inline t3 name##_shared_unchecked(wasm_rt_shared_memory_t* mem, \ + u64 addr) { \ + ATOMIC_ALIGNMENT_CHECK(addr, t1); \ + t1 result; \ + result = \ + atomic_load((_Atomic volatile t1*)MEM_ADDR(mem, addr, sizeof(t1))); \ + force_read(result); \ + return (t3)(t2)result; \ + } \ + DEF_MEM_CHECKS0(name##_shared, _shared_, t1, return, t3) + +DEFINE_ATOMIC_LOAD(i32_atomic_load, u32, u32, u32, FORCE_READ_INT) +DEFINE_ATOMIC_LOAD(i64_atomic_load, u64, u64, u64, FORCE_READ_INT) +DEFINE_ATOMIC_LOAD(i32_atomic_load8_u, u8, u32, u32, FORCE_READ_INT) +DEFINE_ATOMIC_LOAD(i64_atomic_load8_u, u8, u64, u64, FORCE_READ_INT) +DEFINE_ATOMIC_LOAD(i32_atomic_load16_u, u16, u32, u32, FORCE_READ_INT) +DEFINE_ATOMIC_LOAD(i64_atomic_load16_u, u16, u64, u64, FORCE_READ_INT) +DEFINE_ATOMIC_LOAD(i64_atomic_load32_u, u32, u64, u64, FORCE_READ_INT) + +#define DEFINE_ATOMIC_STORE(name, t1, t2) \ + static inline void name##_unchecked(wasm_rt_memory_t* mem, u64 addr, \ + t2 value) { \ + ATOMIC_ALIGNMENT_CHECK(addr, t1); \ + t1 wrapped = (t1)value; \ + wasm_rt_memcpy(MEM_ADDR(mem, addr, sizeof(t1)), &wrapped, sizeof(t1)); \ + } \ + DEF_MEM_CHECKS1(name, _, t1, , void, t2) \ + static inline void name##_shared_unchecked(wasm_rt_shared_memory_t* mem, \ + u64 addr, t2 value) { \ + ATOMIC_ALIGNMENT_CHECK(addr, t1); \ + t1 wrapped = (t1)value; \ + atomic_store((_Atomic volatile t1*)MEM_ADDR(mem, addr, sizeof(t1)), \ + wrapped); \ + } \ + DEF_MEM_CHECKS1(name##_shared, _shared_, t1, , void, t2) + +DEFINE_ATOMIC_STORE(i32_atomic_store, u32, u32) +DEFINE_ATOMIC_STORE(i64_atomic_store, u64, u64) +DEFINE_ATOMIC_STORE(i32_atomic_store8, u8, u32) +DEFINE_ATOMIC_STORE(i32_atomic_store16, u16, u32) +DEFINE_ATOMIC_STORE(i64_atomic_store8, u8, u64) +DEFINE_ATOMIC_STORE(i64_atomic_store16, u16, u64) +DEFINE_ATOMIC_STORE(i64_atomic_store32, u32, u64) + +#define DEFINE_ATOMIC_RMW(name, opname, op, t1, t2) \ + static inline t2 name##_unchecked(wasm_rt_memory_t* mem, u64 addr, \ + t2 value) { \ + ATOMIC_ALIGNMENT_CHECK(addr, t1); \ + t1 wrapped = (t1)value; \ + t1 ret; \ + wasm_rt_memcpy(&ret, MEM_ADDR(mem, addr, sizeof(t1)), sizeof(t1)); \ + ret = ret op wrapped; \ + wasm_rt_memcpy(MEM_ADDR(mem, addr, sizeof(t1)), &ret, sizeof(t1)); \ + return (t2)ret; \ + } \ + DEF_MEM_CHECKS1(name, _, t1, return, t2, t2) \ + static inline t2 name##_shared_unchecked(wasm_rt_shared_memory_t* mem, \ + u64 addr, t2 value) { \ + ATOMIC_ALIGNMENT_CHECK(addr, t1); \ + t1 wrapped = (t1)value; \ + t1 ret = atomic_##opname( \ + (_Atomic volatile t1*)MEM_ADDR(mem, addr, sizeof(t1)), wrapped); \ + return (t2)ret; \ + } \ + DEF_MEM_CHECKS1(name##_shared, _shared_, t1, return, t2, t2) + +DEFINE_ATOMIC_RMW(i32_atomic_rmw8_add_u, fetch_add, +, u8, u32) +DEFINE_ATOMIC_RMW(i32_atomic_rmw16_add_u, fetch_add, +, u16, u32) +DEFINE_ATOMIC_RMW(i32_atomic_rmw_add, fetch_add, +, u32, u32) +DEFINE_ATOMIC_RMW(i64_atomic_rmw8_add_u, fetch_add, +, u8, u64) +DEFINE_ATOMIC_RMW(i64_atomic_rmw16_add_u, fetch_add, +, u16, u64) +DEFINE_ATOMIC_RMW(i64_atomic_rmw32_add_u, fetch_add, +, u32, u64) +DEFINE_ATOMIC_RMW(i64_atomic_rmw_add, fetch_add, +, u64, u64) + +DEFINE_ATOMIC_RMW(i32_atomic_rmw8_sub_u, fetch_sub, -, u8, u32) +DEFINE_ATOMIC_RMW(i32_atomic_rmw16_sub_u, fetch_sub, -, u16, u32) +DEFINE_ATOMIC_RMW(i32_atomic_rmw_sub, fetch_sub, -, u32, u32) +DEFINE_ATOMIC_RMW(i64_atomic_rmw8_sub_u, fetch_sub, -, u8, u64) +DEFINE_ATOMIC_RMW(i64_atomic_rmw16_sub_u, fetch_sub, -, u16, u64) +DEFINE_ATOMIC_RMW(i64_atomic_rmw32_sub_u, fetch_sub, -, u32, u64) +DEFINE_ATOMIC_RMW(i64_atomic_rmw_sub, fetch_sub, -, u64, u64) + +DEFINE_ATOMIC_RMW(i32_atomic_rmw8_and_u, fetch_and, &, u8, u32) +DEFINE_ATOMIC_RMW(i32_atomic_rmw16_and_u, fetch_and, &, u16, u32) +DEFINE_ATOMIC_RMW(i32_atomic_rmw_and, fetch_and, &, u32, u32) +DEFINE_ATOMIC_RMW(i64_atomic_rmw8_and_u, fetch_and, &, u8, u64) +DEFINE_ATOMIC_RMW(i64_atomic_rmw16_and_u, fetch_and, &, u16, u64) +DEFINE_ATOMIC_RMW(i64_atomic_rmw32_and_u, fetch_and, &, u32, u64) +DEFINE_ATOMIC_RMW(i64_atomic_rmw_and, fetch_and, &, u64, u64) + +DEFINE_ATOMIC_RMW(i32_atomic_rmw8_or_u, fetch_or, |, u8, u32) +DEFINE_ATOMIC_RMW(i32_atomic_rmw16_or_u, fetch_or, |, u16, u32) +DEFINE_ATOMIC_RMW(i32_atomic_rmw_or, fetch_or, |, u32, u32) +DEFINE_ATOMIC_RMW(i64_atomic_rmw8_or_u, fetch_or, |, u8, u64) +DEFINE_ATOMIC_RMW(i64_atomic_rmw16_or_u, fetch_or, |, u16, u64) +DEFINE_ATOMIC_RMW(i64_atomic_rmw32_or_u, fetch_or, |, u32, u64) +DEFINE_ATOMIC_RMW(i64_atomic_rmw_or, fetch_or, |, u64, u64) + +DEFINE_ATOMIC_RMW(i32_atomic_rmw8_xor_u, fetch_xor, ^, u8, u32) +DEFINE_ATOMIC_RMW(i32_atomic_rmw16_xor_u, fetch_xor, ^, u16, u32) +DEFINE_ATOMIC_RMW(i32_atomic_rmw_xor, fetch_xor, ^, u32, u32) +DEFINE_ATOMIC_RMW(i64_atomic_rmw8_xor_u, fetch_xor, ^, u8, u64) +DEFINE_ATOMIC_RMW(i64_atomic_rmw16_xor_u, fetch_xor, ^, u16, u64) +DEFINE_ATOMIC_RMW(i64_atomic_rmw32_xor_u, fetch_xor, ^, u32, u64) +DEFINE_ATOMIC_RMW(i64_atomic_rmw_xor, fetch_xor, ^, u64, u64) + +#define DEFINE_ATOMIC_XCHG(name, opname, t1, t2) \ + static inline t2 name##_unchecked(wasm_rt_memory_t* mem, u64 addr, \ + t2 value) { \ + ATOMIC_ALIGNMENT_CHECK(addr, t1); \ + t1 wrapped = (t1)value; \ + t1 ret; \ + wasm_rt_memcpy(&ret, MEM_ADDR(mem, addr, sizeof(t1)), sizeof(t1)); \ + wasm_rt_memcpy(MEM_ADDR(mem, addr, sizeof(t1)), &wrapped, sizeof(t1)); \ + return (t2)ret; \ + } \ + DEF_MEM_CHECKS1(name, _, t1, return, t2, t2) \ + static inline t2 name##_shared_unchecked(wasm_rt_shared_memory_t* mem, \ + u64 addr, t2 value) { \ + ATOMIC_ALIGNMENT_CHECK(addr, t1); \ + t1 wrapped = (t1)value; \ + t1 ret = atomic_##opname( \ + (_Atomic volatile t1*)MEM_ADDR(mem, addr, sizeof(t1)), wrapped); \ + return (t2)ret; \ + } \ + DEF_MEM_CHECKS1(name##_shared, _shared_, t1, return, t2, t2) + +DEFINE_ATOMIC_XCHG(i32_atomic_rmw8_xchg_u, exchange, u8, u32) +DEFINE_ATOMIC_XCHG(i32_atomic_rmw16_xchg_u, exchange, u16, u32) +DEFINE_ATOMIC_XCHG(i32_atomic_rmw_xchg, exchange, u32, u32) +DEFINE_ATOMIC_XCHG(i64_atomic_rmw8_xchg_u, exchange, u8, u64) +DEFINE_ATOMIC_XCHG(i64_atomic_rmw16_xchg_u, exchange, u16, u64) +DEFINE_ATOMIC_XCHG(i64_atomic_rmw32_xchg_u, exchange, u32, u64) +DEFINE_ATOMIC_XCHG(i64_atomic_rmw_xchg, exchange, u64, u64) + +#define DEFINE_ATOMIC_CMP_XCHG(name, t1, t2) \ + static inline t1 name##_unchecked(wasm_rt_memory_t* mem, u64 addr, \ + t1 expected, t1 replacement) { \ + ATOMIC_ALIGNMENT_CHECK(addr, t2); \ + t2 expected_wrapped = (t2)expected; \ + t2 replacement_wrapped = (t2)replacement; \ + t2 ret; \ + wasm_rt_memcpy(&ret, MEM_ADDR(mem, addr, sizeof(t2)), sizeof(t2)); \ + if (ret == expected_wrapped) { \ + wasm_rt_memcpy(MEM_ADDR(mem, addr, sizeof(t2)), &replacement_wrapped, \ + sizeof(t2)); \ + } \ + return (t1)expected_wrapped; \ + } \ + DEF_MEM_CHECKS2(name, _, t2, return, t1, t1, t1) \ + static inline t1 name##_shared_unchecked( \ + wasm_rt_shared_memory_t* mem, u64 addr, t1 expected, t1 replacement) { \ + ATOMIC_ALIGNMENT_CHECK(addr, t2); \ + t2 expected_wrapped = (t2)expected; \ + t2 replacement_wrapped = (t2)replacement; \ + atomic_compare_exchange_strong( \ + (_Atomic volatile t2*)MEM_ADDR(mem, addr, sizeof(t2)), \ + &expected_wrapped, replacement_wrapped); \ + return (t1)expected_wrapped; \ + } \ + DEF_MEM_CHECKS2(name##_shared, _shared_, t2, return, t1, t1, t1) + +DEFINE_ATOMIC_CMP_XCHG(i32_atomic_rmw8_cmpxchg_u, u32, u8); +DEFINE_ATOMIC_CMP_XCHG(i32_atomic_rmw16_cmpxchg_u, u32, u16); +DEFINE_ATOMIC_CMP_XCHG(i32_atomic_rmw_cmpxchg, u32, u32); +DEFINE_ATOMIC_CMP_XCHG(i64_atomic_rmw8_cmpxchg_u, u64, u8); +DEFINE_ATOMIC_CMP_XCHG(i64_atomic_rmw16_cmpxchg_u, u64, u16); +DEFINE_ATOMIC_CMP_XCHG(i64_atomic_rmw32_cmpxchg_u, u64, u32); +DEFINE_ATOMIC_CMP_XCHG(i64_atomic_rmw_cmpxchg, u64, u64); + +#define atomic_fence() atomic_thread_fence(memory_order_seq_cst) diff --git a/src/tools/wasm2c/templates/wasm2c_simd.declarations.c b/src/tools/wasm2c/templates/wasm2c_simd.declarations.c new file mode 100644 index 00000000000..48000f4e74c --- /dev/null +++ b/src/tools/wasm2c/templates/wasm2c_simd.declarations.c @@ -0,0 +1,342 @@ +#if defined(__GNUC__) && defined(__x86_64__) +#define SIMD_FORCE_READ(var) __asm__("" ::"x"(var)); +#elif defined(__GNUC__) && defined(__aarch64__) +#define SIMD_FORCE_READ(var) __asm__("" ::"w"(var)); +#elif defined(__s390x__) +#define SIMD_FORCE_READ(var) __asm__("" ::"d"(var)); +#else +#define SIMD_FORCE_READ(var) +#endif +// TODO: equivalent constraint for ARM and other architectures + +#define DEFINE_SIMD_LOAD_FUNC(name, func, t) \ + static inline v128 name##_unchecked(wasm_rt_memory_t* mem, u64 addr) { \ + v128 result = func(MEM_ADDR(mem, addr, sizeof(t))); \ + SIMD_FORCE_READ(result); \ + return result; \ + } \ + DEF_MEM_CHECKS0(name, _, t, return, v128); + +#define DEFINE_SIMD_LOAD_LANE(name, func, t, lane) \ + static inline v128 name##_unchecked(wasm_rt_memory_t* mem, u64 addr, \ + v128 vec) { \ + v128 result = func(MEM_ADDR(mem, addr, sizeof(t)), vec, lane); \ + SIMD_FORCE_READ(result); \ + return result; \ + } \ + DEF_MEM_CHECKS1(name, _, t, return, v128, v128); + +#define DEFINE_SIMD_STORE(name, t) \ + static inline void name##_unchecked(wasm_rt_memory_t* mem, u64 addr, \ + v128 value) { \ + simde_wasm_v128_store(MEM_ADDR(mem, addr, sizeof(t)), value); \ + } \ + DEF_MEM_CHECKS1(name, _, t, , void, v128); + +#define DEFINE_SIMD_STORE_LANE(name, func, t, lane) \ + static inline void name##_unchecked(wasm_rt_memory_t* mem, u64 addr, \ + v128 value) { \ + func(MEM_ADDR(mem, addr, sizeof(t)), value, lane); \ + } \ + DEF_MEM_CHECKS1(name, _, t, , void, v128); + +// clang-format off +#if WASM_RT_BIG_ENDIAN +static inline v128 v128_impl_load32_zero(const void* a) { + return simde_wasm_i8x16_swizzle( + simde_wasm_v128_load32_zero(a), + simde_wasm_i8x16_const(12,13,14,15,8,9,10,11,4,5,6,7,0,1,2,3)); +} +static inline v128 v128_impl_load64_zero(const void* a) { + return simde_wasm_i8x16_swizzle( + simde_wasm_v128_load64_zero(a), + simde_wasm_i8x16_const(8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7)); +} +#else +#define v128_impl_load32_zero simde_wasm_v128_load32_zero +#define v128_impl_load64_zero simde_wasm_v128_load64_zero +#endif + +DEFINE_SIMD_LOAD_FUNC(v128_load, simde_wasm_v128_load, v128) + +DEFINE_SIMD_LOAD_FUNC(v128_load8_splat, simde_wasm_v128_load8_splat, u8) +DEFINE_SIMD_LOAD_FUNC(v128_load16_splat, simde_wasm_v128_load16_splat, u16) +DEFINE_SIMD_LOAD_FUNC(v128_load32_splat, simde_wasm_v128_load32_splat, u32) +DEFINE_SIMD_LOAD_FUNC(v128_load64_splat, simde_wasm_v128_load64_splat, u64) + +DEFINE_SIMD_LOAD_FUNC(i16x8_load8x8, simde_wasm_i16x8_load8x8, u64) +DEFINE_SIMD_LOAD_FUNC(u16x8_load8x8, simde_wasm_u16x8_load8x8, u64) +DEFINE_SIMD_LOAD_FUNC(i32x4_load16x4, simde_wasm_i32x4_load16x4, u64) +DEFINE_SIMD_LOAD_FUNC(u32x4_load16x4, simde_wasm_u32x4_load16x4, u64) +DEFINE_SIMD_LOAD_FUNC(i64x2_load32x2, simde_wasm_i64x2_load32x2, u64) +DEFINE_SIMD_LOAD_FUNC(u64x2_load32x2, simde_wasm_u64x2_load32x2, u64) + +DEFINE_SIMD_LOAD_FUNC(v128_load32_zero, v128_impl_load32_zero, u32) +DEFINE_SIMD_LOAD_FUNC(v128_load64_zero, v128_impl_load64_zero, u64) + +#if WASM_RT_BIG_ENDIAN +DEFINE_SIMD_LOAD_LANE(v128_load8_lane0, simde_wasm_v128_load8_lane, u8, 15) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane1, simde_wasm_v128_load8_lane, u8, 14) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane2, simde_wasm_v128_load8_lane, u8, 13) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane3, simde_wasm_v128_load8_lane, u8, 12) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane4, simde_wasm_v128_load8_lane, u8, 11) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane5, simde_wasm_v128_load8_lane, u8, 10) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane6, simde_wasm_v128_load8_lane, u8, 9) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane7, simde_wasm_v128_load8_lane, u8, 8) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane8, simde_wasm_v128_load8_lane, u8, 7) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane9, simde_wasm_v128_load8_lane, u8, 6) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane10, simde_wasm_v128_load8_lane, u8, 5) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane11, simde_wasm_v128_load8_lane, u8, 4) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane12, simde_wasm_v128_load8_lane, u8, 3) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane13, simde_wasm_v128_load8_lane, u8, 2) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane14, simde_wasm_v128_load8_lane, u8, 1) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane15, simde_wasm_v128_load8_lane, u8, 0) +DEFINE_SIMD_LOAD_LANE(v128_load16_lane0, simde_wasm_v128_load16_lane, u16, 7) +DEFINE_SIMD_LOAD_LANE(v128_load16_lane1, simde_wasm_v128_load16_lane, u16, 6) +DEFINE_SIMD_LOAD_LANE(v128_load16_lane2, simde_wasm_v128_load16_lane, u16, 5) +DEFINE_SIMD_LOAD_LANE(v128_load16_lane3, simde_wasm_v128_load16_lane, u16, 4) +DEFINE_SIMD_LOAD_LANE(v128_load16_lane4, simde_wasm_v128_load16_lane, u16, 3) +DEFINE_SIMD_LOAD_LANE(v128_load16_lane5, simde_wasm_v128_load16_lane, u16, 2) +DEFINE_SIMD_LOAD_LANE(v128_load16_lane6, simde_wasm_v128_load16_lane, u16, 1) +DEFINE_SIMD_LOAD_LANE(v128_load16_lane7, simde_wasm_v128_load16_lane, u16, 0) +DEFINE_SIMD_LOAD_LANE(v128_load32_lane0, simde_wasm_v128_load32_lane, u32, 3) +DEFINE_SIMD_LOAD_LANE(v128_load32_lane1, simde_wasm_v128_load32_lane, u32, 2) +DEFINE_SIMD_LOAD_LANE(v128_load32_lane2, simde_wasm_v128_load32_lane, u32, 1) +DEFINE_SIMD_LOAD_LANE(v128_load32_lane3, simde_wasm_v128_load32_lane, u32, 0) +DEFINE_SIMD_LOAD_LANE(v128_load64_lane0, simde_wasm_v128_load64_lane, u64, 1) +DEFINE_SIMD_LOAD_LANE(v128_load64_lane1, simde_wasm_v128_load64_lane, u64, 0) +#else +DEFINE_SIMD_LOAD_LANE(v128_load8_lane0, simde_wasm_v128_load8_lane, u8, 0) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane1, simde_wasm_v128_load8_lane, u8, 1) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane2, simde_wasm_v128_load8_lane, u8, 2) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane3, simde_wasm_v128_load8_lane, u8, 3) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane4, simde_wasm_v128_load8_lane, u8, 4) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane5, simde_wasm_v128_load8_lane, u8, 5) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane6, simde_wasm_v128_load8_lane, u8, 6) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane7, simde_wasm_v128_load8_lane, u8, 7) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane8, simde_wasm_v128_load8_lane, u8, 8) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane9, simde_wasm_v128_load8_lane, u8, 9) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane10, simde_wasm_v128_load8_lane, u8, 10) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane11, simde_wasm_v128_load8_lane, u8, 11) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane12, simde_wasm_v128_load8_lane, u8, 12) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane13, simde_wasm_v128_load8_lane, u8, 13) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane14, simde_wasm_v128_load8_lane, u8, 14) +DEFINE_SIMD_LOAD_LANE(v128_load8_lane15, simde_wasm_v128_load8_lane, u8, 15) +DEFINE_SIMD_LOAD_LANE(v128_load16_lane0, simde_wasm_v128_load16_lane, u16, 0) +DEFINE_SIMD_LOAD_LANE(v128_load16_lane1, simde_wasm_v128_load16_lane, u16, 1) +DEFINE_SIMD_LOAD_LANE(v128_load16_lane2, simde_wasm_v128_load16_lane, u16, 2) +DEFINE_SIMD_LOAD_LANE(v128_load16_lane3, simde_wasm_v128_load16_lane, u16, 3) +DEFINE_SIMD_LOAD_LANE(v128_load16_lane4, simde_wasm_v128_load16_lane, u16, 4) +DEFINE_SIMD_LOAD_LANE(v128_load16_lane5, simde_wasm_v128_load16_lane, u16, 5) +DEFINE_SIMD_LOAD_LANE(v128_load16_lane6, simde_wasm_v128_load16_lane, u16, 6) +DEFINE_SIMD_LOAD_LANE(v128_load16_lane7, simde_wasm_v128_load16_lane, u16, 7) +DEFINE_SIMD_LOAD_LANE(v128_load32_lane0, simde_wasm_v128_load32_lane, u32, 0) +DEFINE_SIMD_LOAD_LANE(v128_load32_lane1, simde_wasm_v128_load32_lane, u32, 1) +DEFINE_SIMD_LOAD_LANE(v128_load32_lane2, simde_wasm_v128_load32_lane, u32, 2) +DEFINE_SIMD_LOAD_LANE(v128_load32_lane3, simde_wasm_v128_load32_lane, u32, 3) +DEFINE_SIMD_LOAD_LANE(v128_load64_lane0, simde_wasm_v128_load64_lane, u64, 0) +DEFINE_SIMD_LOAD_LANE(v128_load64_lane1, simde_wasm_v128_load64_lane, u64, 1) +#endif + +DEFINE_SIMD_STORE(v128_store, v128) + +#if WASM_RT_BIG_ENDIAN +DEFINE_SIMD_STORE_LANE(v128_store8_lane0, simde_wasm_v128_store8_lane, u8, 15) +DEFINE_SIMD_STORE_LANE(v128_store8_lane1, simde_wasm_v128_store8_lane, u8, 14) +DEFINE_SIMD_STORE_LANE(v128_store8_lane2, simde_wasm_v128_store8_lane, u8, 13) +DEFINE_SIMD_STORE_LANE(v128_store8_lane3, simde_wasm_v128_store8_lane, u8, 12) +DEFINE_SIMD_STORE_LANE(v128_store8_lane4, simde_wasm_v128_store8_lane, u8, 11) +DEFINE_SIMD_STORE_LANE(v128_store8_lane5, simde_wasm_v128_store8_lane, u8, 10) +DEFINE_SIMD_STORE_LANE(v128_store8_lane6, simde_wasm_v128_store8_lane, u8, 9) +DEFINE_SIMD_STORE_LANE(v128_store8_lane7, simde_wasm_v128_store8_lane, u8, 8) +DEFINE_SIMD_STORE_LANE(v128_store8_lane8, simde_wasm_v128_store8_lane, u8, 7) +DEFINE_SIMD_STORE_LANE(v128_store8_lane9, simde_wasm_v128_store8_lane, u8, 6) +DEFINE_SIMD_STORE_LANE(v128_store8_lane10, simde_wasm_v128_store8_lane, u8, 5) +DEFINE_SIMD_STORE_LANE(v128_store8_lane11, simde_wasm_v128_store8_lane, u8, 4) +DEFINE_SIMD_STORE_LANE(v128_store8_lane12, simde_wasm_v128_store8_lane, u8, 3) +DEFINE_SIMD_STORE_LANE(v128_store8_lane13, simde_wasm_v128_store8_lane, u8, 2) +DEFINE_SIMD_STORE_LANE(v128_store8_lane14, simde_wasm_v128_store8_lane, u8, 1) +DEFINE_SIMD_STORE_LANE(v128_store8_lane15, simde_wasm_v128_store8_lane, u8, 0) +DEFINE_SIMD_STORE_LANE(v128_store16_lane0, simde_wasm_v128_store16_lane, u16, 7) +DEFINE_SIMD_STORE_LANE(v128_store16_lane1, simde_wasm_v128_store16_lane, u16, 6) +DEFINE_SIMD_STORE_LANE(v128_store16_lane2, simde_wasm_v128_store16_lane, u16, 5) +DEFINE_SIMD_STORE_LANE(v128_store16_lane3, simde_wasm_v128_store16_lane, u16, 4) +DEFINE_SIMD_STORE_LANE(v128_store16_lane4, simde_wasm_v128_store16_lane, u16, 3) +DEFINE_SIMD_STORE_LANE(v128_store16_lane5, simde_wasm_v128_store16_lane, u16, 2) +DEFINE_SIMD_STORE_LANE(v128_store16_lane6, simde_wasm_v128_store16_lane, u16, 1) +DEFINE_SIMD_STORE_LANE(v128_store16_lane7, simde_wasm_v128_store16_lane, u16, 0) +DEFINE_SIMD_STORE_LANE(v128_store32_lane0, simde_wasm_v128_store32_lane, u32, 3) +DEFINE_SIMD_STORE_LANE(v128_store32_lane1, simde_wasm_v128_store32_lane, u32, 2) +DEFINE_SIMD_STORE_LANE(v128_store32_lane2, simde_wasm_v128_store32_lane, u32, 1) +DEFINE_SIMD_STORE_LANE(v128_store32_lane3, simde_wasm_v128_store32_lane, u32, 0) +DEFINE_SIMD_STORE_LANE(v128_store64_lane0, simde_wasm_v128_store64_lane, u64, 1) +DEFINE_SIMD_STORE_LANE(v128_store64_lane1, simde_wasm_v128_store64_lane, u64, 0) +#else +DEFINE_SIMD_STORE_LANE(v128_store8_lane0, simde_wasm_v128_store8_lane, u8, 0) +DEFINE_SIMD_STORE_LANE(v128_store8_lane1, simde_wasm_v128_store8_lane, u8, 1) +DEFINE_SIMD_STORE_LANE(v128_store8_lane2, simde_wasm_v128_store8_lane, u8, 2) +DEFINE_SIMD_STORE_LANE(v128_store8_lane3, simde_wasm_v128_store8_lane, u8, 3) +DEFINE_SIMD_STORE_LANE(v128_store8_lane4, simde_wasm_v128_store8_lane, u8, 4) +DEFINE_SIMD_STORE_LANE(v128_store8_lane5, simde_wasm_v128_store8_lane, u8, 5) +DEFINE_SIMD_STORE_LANE(v128_store8_lane6, simde_wasm_v128_store8_lane, u8, 6) +DEFINE_SIMD_STORE_LANE(v128_store8_lane7, simde_wasm_v128_store8_lane, u8, 7) +DEFINE_SIMD_STORE_LANE(v128_store8_lane8, simde_wasm_v128_store8_lane, u8, 8) +DEFINE_SIMD_STORE_LANE(v128_store8_lane9, simde_wasm_v128_store8_lane, u8, 9) +DEFINE_SIMD_STORE_LANE(v128_store8_lane10, simde_wasm_v128_store8_lane, u8, 10) +DEFINE_SIMD_STORE_LANE(v128_store8_lane11, simde_wasm_v128_store8_lane, u8, 11) +DEFINE_SIMD_STORE_LANE(v128_store8_lane12, simde_wasm_v128_store8_lane, u8, 12) +DEFINE_SIMD_STORE_LANE(v128_store8_lane13, simde_wasm_v128_store8_lane, u8, 13) +DEFINE_SIMD_STORE_LANE(v128_store8_lane14, simde_wasm_v128_store8_lane, u8, 14) +DEFINE_SIMD_STORE_LANE(v128_store8_lane15, simde_wasm_v128_store8_lane, u8, 15) +DEFINE_SIMD_STORE_LANE(v128_store16_lane0, simde_wasm_v128_store16_lane, u16, 0) +DEFINE_SIMD_STORE_LANE(v128_store16_lane1, simde_wasm_v128_store16_lane, u16, 1) +DEFINE_SIMD_STORE_LANE(v128_store16_lane2, simde_wasm_v128_store16_lane, u16, 2) +DEFINE_SIMD_STORE_LANE(v128_store16_lane3, simde_wasm_v128_store16_lane, u16, 3) +DEFINE_SIMD_STORE_LANE(v128_store16_lane4, simde_wasm_v128_store16_lane, u16, 4) +DEFINE_SIMD_STORE_LANE(v128_store16_lane5, simde_wasm_v128_store16_lane, u16, 5) +DEFINE_SIMD_STORE_LANE(v128_store16_lane6, simde_wasm_v128_store16_lane, u16, 6) +DEFINE_SIMD_STORE_LANE(v128_store16_lane7, simde_wasm_v128_store16_lane, u16, 7) +DEFINE_SIMD_STORE_LANE(v128_store32_lane0, simde_wasm_v128_store32_lane, u32, 0) +DEFINE_SIMD_STORE_LANE(v128_store32_lane1, simde_wasm_v128_store32_lane, u32, 1) +DEFINE_SIMD_STORE_LANE(v128_store32_lane2, simde_wasm_v128_store32_lane, u32, 2) +DEFINE_SIMD_STORE_LANE(v128_store32_lane3, simde_wasm_v128_store32_lane, u32, 3) +DEFINE_SIMD_STORE_LANE(v128_store64_lane0, simde_wasm_v128_store64_lane, u64, 0) +DEFINE_SIMD_STORE_LANE(v128_store64_lane1, simde_wasm_v128_store64_lane, u64, 1) +#endif + +#if WASM_RT_BIG_ENDIAN +#define v128_const(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) simde_wasm_i8x16_const(p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a) +#define v128_i8x16_extract_lane(v, l) simde_wasm_i8x16_extract_lane(v, 15-(l)) +#define v128_u8x16_extract_lane(v, l) simde_wasm_u8x16_extract_lane(v, 15-(l)) +#define v128_i16x8_extract_lane(v, l) simde_wasm_i16x8_extract_lane(v, 7-(l)) +#define v128_u16x8_extract_lane(v, l) simde_wasm_u16x8_extract_lane(v, 7-(l)) +#define v128_i32x4_extract_lane(v, l) simde_wasm_i32x4_extract_lane(v, 3-(l)) +#define v128_i64x2_extract_lane(v, l) simde_wasm_i64x2_extract_lane(v, 1-(l)) +#define v128_f32x4_extract_lane(v, l) simde_wasm_f32x4_extract_lane(v, 3-(l)) +#define v128_f64x2_extract_lane(v, l) simde_wasm_f64x2_extract_lane(v, 1-(l)) +#define v128_i8x16_replace_lane(v, l, x) simde_wasm_i8x16_replace_lane(v, 15-(l), x) +#define v128_u8x16_replace_lane(v, l, x) simde_wasm_u8x16_replace_lane(v, 15-(l), x) +#define v128_i16x8_replace_lane(v, l, x) simde_wasm_i16x8_replace_lane(v, 7-(l), x) +#define v128_u16x8_replace_lane(v, l, x) simde_wasm_u16x8_replace_lane(v, 7-(l), x) +#define v128_i32x4_replace_lane(v, l, x) simde_wasm_i32x4_replace_lane(v, 3-(l), x) +#define v128_i64x2_replace_lane(v, l, x) simde_wasm_i64x2_replace_lane(v, 1-(l), x) +#define v128_f32x4_replace_lane(v, l, x) simde_wasm_f32x4_replace_lane(v, 3-(l), x) +#define v128_f64x2_replace_lane(v, l, x) simde_wasm_f64x2_replace_lane(v, 1-(l), x) +#define v128_i8x16_bitmask(v) simde_wasm_i8x16_bitmask(simde_wasm_i8x16_swizzle(v, simde_wasm_i8x16_const(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0))) +#define v128_i16x8_bitmask(v) simde_wasm_i16x8_bitmask(simde_wasm_i8x16_swizzle(v, simde_wasm_i8x16_const(14,15,12,13,10,11,8,9,6,7,4,5,2,3,0,1))) +#define v128_i32x4_bitmask(v) simde_wasm_i32x4_bitmask(simde_wasm_i8x16_swizzle(v, simde_wasm_i8x16_const(12,13,14,15,8,9,10,11,4,5,6,7,0,1,2,3))) +#define v128_i64x2_bitmask(v) simde_wasm_i64x2_bitmask(simde_wasm_i8x16_swizzle(v, simde_wasm_i8x16_const(8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7))) +#define v128_i8x16_swizzle(v1, v2) simde_wasm_i8x16_swizzle(v1, simde_wasm_v128_xor(v2, simde_wasm_i8x16_splat(15))) +#define v128_i8x16_shuffle(v1,v2,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) simde_wasm_i8x16_shuffle(v2,v1,31-(p),31-(o),31-(n),31-(m),31-(l),31-(k),31-(j),31-(i),31-(h),31-(g),31-(f),31-(e),31-(d),31-(c),31-(b),31-(a)) +#define v128_i16x8_extmul_high_i8x16 simde_wasm_i16x8_extmul_low_i8x16 +#define v128_u16x8_extmul_high_u8x16 simde_wasm_u16x8_extmul_low_u8x16 +#define v128_i16x8_extmul_low_i8x16 simde_wasm_i16x8_extmul_high_i8x16 +#define v128_u16x8_extmul_low_u8x16 simde_wasm_u16x8_extmul_high_u8x16 +#define v128_i32x4_extmul_high_i16x8 simde_wasm_i32x4_extmul_low_i16x8 +#define v128_u32x4_extmul_high_u16x8 simde_wasm_u32x4_extmul_low_u16x8 +#define v128_i32x4_extmul_low_i16x8 simde_wasm_i32x4_extmul_high_i16x8 +#define v128_u32x4_extmul_low_u16x8 simde_wasm_u32x4_extmul_high_u16x8 +#define v128_i64x2_extmul_high_i32x4 simde_wasm_i64x2_extmul_low_i32x4 +#define v128_u64x2_extmul_high_u32x4 simde_wasm_u64x2_extmul_low_u32x4 +#define v128_i64x2_extmul_low_i32x4 simde_wasm_i64x2_extmul_high_i32x4 +#define v128_u64x2_extmul_low_u32x4 simde_wasm_u64x2_extmul_high_u32x4 +#define v128_i16x8_extend_high_i8x16 simde_wasm_i16x8_extend_low_i8x16 +#define v128_u16x8_extend_high_u8x16 simde_wasm_u16x8_extend_low_u8x16 +#define v128_i16x8_extend_low_i8x16 simde_wasm_i16x8_extend_high_i8x16 +#define v128_u16x8_extend_low_u8x16 simde_wasm_u16x8_extend_high_u8x16 +#define v128_i32x4_extend_high_i16x8 simde_wasm_i32x4_extend_low_i16x8 +#define v128_u32x4_extend_high_u16x8 simde_wasm_u32x4_extend_low_u16x8 +#define v128_i32x4_extend_low_i16x8 simde_wasm_i32x4_extend_high_i16x8 +#define v128_u32x4_extend_low_u16x8 simde_wasm_u32x4_extend_high_u16x8 +#define v128_i64x2_extend_high_i32x4 simde_wasm_i64x2_extend_low_i32x4 +#define v128_u64x2_extend_high_u32x4 simde_wasm_u64x2_extend_low_u32x4 +#define v128_i64x2_extend_low_i32x4 simde_wasm_i64x2_extend_high_i32x4 +#define v128_u64x2_extend_low_u32x4 simde_wasm_u64x2_extend_high_u32x4 +#define v128_i32x4_trunc_sat_f64x2_zero(a) \ + simde_wasm_i8x16_swizzle( \ + simde_wasm_i32x4_trunc_sat_f64x2_zero(a), \ + simde_wasm_i8x16_const(8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7)) +#define v128_u32x4_trunc_sat_f64x2_zero(a) \ + simde_wasm_i8x16_swizzle( \ + simde_wasm_u32x4_trunc_sat_f64x2_zero(a), \ + simde_wasm_i8x16_const(8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7)) +#define v128_i16x8_narrow_i32x4(a,b) simde_wasm_i16x8_narrow_i32x4(b,a) +#define v128_u16x8_narrow_i32x4(a,b) simde_wasm_u16x8_narrow_i32x4(b,a) +#define v128_i8x16_narrow_i16x8(a,b) simde_wasm_i8x16_narrow_i16x8(b,a) +#define v128_u8x16_narrow_i16x8(a,b) simde_wasm_u8x16_narrow_i16x8(b,a) +#define v128_f64x2_promote_low_f32x4(a) \ + simde_wasm_f64x2_promote_low_f32x4(simde_wasm_i8x16_swizzle( \ + a, \ + simde_wasm_i8x16_const(8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7))) +#define v128_f32x4_demote_f64x2_zero(a) \ + simde_wasm_i8x16_swizzle( \ + simde_wasm_f32x4_demote_f64x2_zero(a), \ + simde_wasm_i8x16_const(8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7)) +#define v128_f64x2_convert_low_i32x4(a) \ + simde_wasm_f64x2_convert_low_i32x4(simde_wasm_i8x16_swizzle( \ + a, \ + simde_wasm_i8x16_const(8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7))) +#define v128_f64x2_convert_low_u32x4(a) \ + simde_wasm_f64x2_convert_low_u32x4(simde_wasm_i8x16_swizzle( \ + a, \ + simde_wasm_i8x16_const(8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7))) +#else +#define v128_const simde_wasm_i8x16_const +#define v128_i8x16_extract_lane simde_wasm_i8x16_extract_lane +#define v128_u8x16_extract_lane simde_wasm_u8x16_extract_lane +#define v128_i16x8_extract_lane simde_wasm_i16x8_extract_lane +#define v128_u16x8_extract_lane simde_wasm_u16x8_extract_lane +#define v128_i32x4_extract_lane simde_wasm_i32x4_extract_lane +#define v128_i64x2_extract_lane simde_wasm_i64x2_extract_lane +#define v128_f32x4_extract_lane simde_wasm_f32x4_extract_lane +#define v128_f64x2_extract_lane simde_wasm_f64x2_extract_lane +#define v128_i8x16_replace_lane simde_wasm_i8x16_replace_lane +#define v128_u8x16_replace_lane simde_wasm_u8x16_replace_lane +#define v128_i16x8_replace_lane simde_wasm_i16x8_replace_lane +#define v128_u16x8_replace_lane simde_wasm_u16x8_replace_lane +#define v128_i32x4_replace_lane simde_wasm_i32x4_replace_lane +#define v128_i64x2_replace_lane simde_wasm_i64x2_replace_lane +#define v128_f32x4_replace_lane simde_wasm_f32x4_replace_lane +#define v128_f64x2_replace_lane simde_wasm_f64x2_replace_lane +#define v128_i8x16_bitmask simde_wasm_i8x16_bitmask +#define v128_i16x8_bitmask simde_wasm_i16x8_bitmask +#define v128_i32x4_bitmask simde_wasm_i32x4_bitmask +#define v128_i64x2_bitmask simde_wasm_i64x2_bitmask +#define v128_i8x16_swizzle simde_wasm_i8x16_swizzle +#define v128_i8x16_shuffle simde_wasm_i8x16_shuffle +#define v128_i16x8_extmul_high_i8x16 simde_wasm_i16x8_extmul_high_i8x16 +#define v128_u16x8_extmul_high_u8x16 simde_wasm_u16x8_extmul_high_u8x16 +#define v128_i16x8_extmul_low_i8x16 simde_wasm_i16x8_extmul_low_i8x16 +#define v128_u16x8_extmul_low_u8x16 simde_wasm_u16x8_extmul_low_u8x16 +#define v128_i32x4_extmul_high_i16x8 simde_wasm_i32x4_extmul_high_i16x8 +#define v128_u32x4_extmul_high_u16x8 simde_wasm_u32x4_extmul_high_u16x8 +#define v128_i32x4_extmul_low_i16x8 simde_wasm_i32x4_extmul_low_i16x8 +#define v128_u32x4_extmul_low_u16x8 simde_wasm_u32x4_extmul_low_u16x8 +#define v128_i64x2_extmul_high_i32x4 simde_wasm_i64x2_extmul_high_i32x4 +#define v128_u64x2_extmul_high_u32x4 simde_wasm_u64x2_extmul_high_u32x4 +#define v128_i64x2_extmul_low_i32x4 simde_wasm_i64x2_extmul_low_i32x4 +#define v128_u64x2_extmul_low_u32x4 simde_wasm_u64x2_extmul_low_u32x4 +#define v128_i16x8_extend_high_i8x16 simde_wasm_i16x8_extend_high_i8x16 +#define v128_u16x8_extend_high_u8x16 simde_wasm_u16x8_extend_high_u8x16 +#define v128_i16x8_extend_low_i8x16 simde_wasm_i16x8_extend_low_i8x16 +#define v128_u16x8_extend_low_u8x16 simde_wasm_u16x8_extend_low_u8x16 +#define v128_i32x4_extend_high_i16x8 simde_wasm_i32x4_extend_high_i16x8 +#define v128_u32x4_extend_high_u16x8 simde_wasm_u32x4_extend_high_u16x8 +#define v128_i32x4_extend_low_i16x8 simde_wasm_i32x4_extend_low_i16x8 +#define v128_u32x4_extend_low_u16x8 simde_wasm_u32x4_extend_low_u16x8 +#define v128_i64x2_extend_high_i32x4 simde_wasm_i64x2_extend_high_i32x4 +#define v128_u64x2_extend_high_u32x4 simde_wasm_u64x2_extend_high_u32x4 +#define v128_i64x2_extend_low_i32x4 simde_wasm_i64x2_extend_low_i32x4 +#define v128_u64x2_extend_low_u32x4 simde_wasm_u64x2_extend_low_u32x4 +#define v128_i32x4_trunc_sat_f64x2_zero simde_wasm_i32x4_trunc_sat_f64x2_zero +#define v128_u32x4_trunc_sat_f64x2_zero simde_wasm_u32x4_trunc_sat_f64x2_zero +#define v128_i16x8_narrow_i32x4 simde_wasm_i16x8_narrow_i32x4 +#define v128_u16x8_narrow_i32x4 simde_wasm_u16x8_narrow_i32x4 +#define v128_i8x16_narrow_i16x8 simde_wasm_i8x16_narrow_i16x8 +#define v128_u8x16_narrow_i16x8 simde_wasm_u8x16_narrow_i16x8 +#define v128_f64x2_promote_low_f32x4 simde_wasm_f64x2_promote_low_f32x4 +#define v128_f32x4_demote_f64x2_zero simde_wasm_f32x4_demote_f64x2_zero +#define v128_f64x2_convert_low_i32x4 simde_wasm_f64x2_convert_low_i32x4 +#define v128_f64x2_convert_low_u32x4 simde_wasm_f64x2_convert_low_u32x4 +#endif +// clang-format on diff --git a/src/tools/wasm2c/templates/wasm2c_spec.top.c b/src/tools/wasm2c/templates/wasm2c_spec.top.c new file mode 100644 index 00000000000..29c933f57a6 --- /dev/null +++ b/src/tools/wasm2c/templates/wasm2c_spec.top.c @@ -0,0 +1,502 @@ +#include +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wasm-rt.h" +#include "wasm-rt-impl.h" +#include "wasm-rt-exceptions.h" + +/* NOTE: function argument evaluation order is implementation-defined in C, + so it SHOULD NOT be relied on by tests. */ +#if WASM_RT_BIG_ENDIAN +#define v128_i8x16_make(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \ + simde_wasm_i8x16_make(p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a) +#define v128_u8x16_make(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \ + simde_wasm_u8x16_make(p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a) +#define v128_i16x8_make(a,b,c,d,e,f,g,h) simde_wasm_i16x8_make(h,g,f,e,d,c,b,a) +#define v128_u16x8_make(a,b,c,d,e,f,g,h) simde_wasm_u16x8_make(h,g,f,e,d,c,b,a) +#define v128_i32x4_make(a,b,c,d) simde_wasm_i32x4_make(d,c,b,a) +#define v128_u32x4_make(a,b,c,d) simde_wasm_u32x4_make(d,c,b,a) +#define v128_i64x2_make(a,b) simde_wasm_i64x2_make(b,a) +#define v128_u64x2_make(a,b) simde_wasm_u64x2_make(b,a) +#define v128_f32x4_make(a,b,c,d) simde_wasm_f32x4_make(d,c,b,a) +#define v128_f64x2_make(a,b) simde_wasm_f64x2_make(b,a) +#define v128_i8x16_extract_lane(a,n) simde_wasm_u8x16_extract_lane(a,15-(n)) +#define v128_u8x16_extract_lane(a,n) simde_wasm_u8x16_extract_lane(a,15-(n)) +#define v128_i16x8_extract_lane(a,n) simde_wasm_u16x8_extract_lane(a,7-(n)) +#define v128_u16x8_extract_lane(a,n) simde_wasm_u16x8_extract_lane(a,7-(n)) +#define v128_i32x4_extract_lane(a,n) simde_wasm_u32x4_extract_lane(a,3-(n)) +#define v128_u32x4_extract_lane(a,n) simde_wasm_u32x4_extract_lane(a,3-(n)) +#define v128_i64x2_extract_lane(a,n) simde_wasm_u64x2_extract_lane(a,1-(n)) +#define v128_u64x2_extract_lane(a,n) simde_wasm_u64x2_extract_lane(a,1-(n)) +#define v128_f32x4_extract_lane(a,n) simde_wasm_f32x4_extract_lane(a,3-(n)) +#define v128_f64x2_extract_lane(a,n) simde_wasm_f64x2_extract_lane(a,1-(n)) +#else +#define v128_i8x16_make simde_wasm_i8x16_make +#define v128_u8x16_make simde_wasm_u8x16_make +#define v128_i16x8_make simde_wasm_i16x8_make +#define v128_u16x8_make simde_wasm_u16x8_make +#define v128_i32x4_make simde_wasm_i32x4_make +#define v128_u32x4_make simde_wasm_u32x4_make +#define v128_i64x2_make simde_wasm_i64x2_make +#define v128_u64x2_make simde_wasm_u64x2_make +#define v128_f32x4_make simde_wasm_f32x4_make +#define v128_f64x2_make simde_wasm_f64x2_make +// like is_equal_TYPE below, always use unsigned for these +#define v128_i8x16_extract_lane simde_wasm_u8x16_extract_lane +#define v128_u8x16_extract_lane simde_wasm_u8x16_extract_lane +#define v128_i16x8_extract_lane simde_wasm_u16x8_extract_lane +#define v128_u16x8_extract_lane simde_wasm_u16x8_extract_lane +#define v128_i32x4_extract_lane simde_wasm_u32x4_extract_lane +#define v128_u32x4_extract_lane simde_wasm_u32x4_extract_lane +#define v128_i64x2_extract_lane simde_wasm_u64x2_extract_lane +#define v128_u64x2_extract_lane simde_wasm_u64x2_extract_lane +#define v128_f32x4_extract_lane simde_wasm_f32x4_extract_lane +#define v128_f64x2_extract_lane simde_wasm_f64x2_extract_lane +#endif + +static int g_tests_run; +static int g_tests_passed; + +static void run_spec_tests(void); + +static void error(const char* file, int line, const char* format, ...) { + va_list args; + va_start(args, format); + fprintf(stderr, "%s:%d: assertion failed: ", file, line); + vfprintf(stderr, format, args); + va_end(args); +} + +#define ASSERT_EXCEPTION(f) \ + do { \ + g_tests_run++; \ + if (wasm_rt_impl_try() == WASM_RT_TRAP_UNCAUGHT_EXCEPTION) { \ + g_tests_passed++; \ + } else { \ + (void)(f); \ + error(__FILE__, __LINE__, "expected " #f " to throw exception.\n"); \ + } \ + } while (0) + +#define ASSERT_TRAP(f) \ + do { \ + g_tests_run++; \ + if (wasm_rt_impl_try() != 0) { \ + g_tests_passed++; \ + } else { \ + (void)(f); \ + error(__FILE__, __LINE__, "expected " #f " to trap.\n"); \ + } \ + } while (0) + +#define ASSERT_EXHAUSTION(f) \ + do { \ + g_tests_run++; \ + wasm_rt_trap_t code = wasm_rt_impl_try(); \ + switch (code) { \ + case WASM_RT_TRAP_NONE: \ + (void)(f); \ + error(__FILE__, __LINE__, "expected " #f " to trap.\n"); \ + break; \ + case WASM_RT_TRAP_EXHAUSTION: \ + g_tests_passed++; \ + break; \ + default: \ + error(__FILE__, __LINE__, \ + "expected " #f \ + " to trap due to exhaustion, got trap code %d.\n", \ + code); \ + break; \ + } \ + } while (0) + +#define ASSERT_RETURN(f) \ + do { \ + g_tests_run++; \ + int trap_code = wasm_rt_impl_try(); \ + if (trap_code) { \ + error(__FILE__, __LINE__, #f " trapped (%s).\n", \ + wasm_rt_strerror(trap_code)); \ + } else { \ + f; \ + g_tests_passed++; \ + } \ + } while (0) + +#define ASSERT_RETURN_T(type, fmt, f, expected) \ + do { \ + g_tests_run++; \ + int trap_code = wasm_rt_impl_try(); \ + if (trap_code) { \ + error(__FILE__, __LINE__, #f " trapped (%s).\n", \ + wasm_rt_strerror(trap_code)); \ + } else { \ + type actual = f; \ + if (is_equal_##type(actual, expected)) { \ + g_tests_passed++; \ + } else { \ + error(__FILE__, __LINE__, \ + "in " #f ": expected %" fmt ", got %" fmt ".\n", expected, \ + actual); \ + } \ + } \ + } while (0) + +#define ASSERT_RETURN_FUNCREF(f, expected) \ + do { \ + g_tests_run++; \ + int trap_code = wasm_rt_impl_try(); \ + if (trap_code) { \ + error(__FILE__, __LINE__, #f " trapped (%s).\n", \ + wasm_rt_strerror(trap_code)); \ + } else { \ + wasm_rt_funcref_t actual = f; \ + if (is_equal_wasm_rt_funcref_t(actual, expected)) { \ + g_tests_passed++; \ + } else { \ + error(__FILE__, __LINE__, \ + "in " #f ": mismatch between expected and actual funcref"); \ + } \ + } \ + } while (0) + +#define ASSERT_RETURN_EXNREF(f, expected) \ + do { \ + g_tests_run++; \ + int trap_code = wasm_rt_impl_try(); \ + if (trap_code) { \ + error(__FILE__, __LINE__, #f " trapped (%s).\n", \ + wasm_rt_strerror(trap_code)); \ + } else { \ + wasm_rt_exnref_t actual = f; \ + if (is_equal_wasm_rt_exnref_t(actual, expected)) { \ + g_tests_passed++; \ + } else { \ + error(__FILE__, __LINE__, \ + "in " #f ": mismatch between expected and actual exnref"); \ + } \ + } \ + } while (0) + +#define ASSERT_RETURN_NAN_T(type, itype, fmt, f, kind) \ + do { \ + g_tests_run++; \ + int trap_code = wasm_rt_impl_try(); \ + if (trap_code) { \ + error(__FILE__, __LINE__, #f " trapped (%s).\n", \ + wasm_rt_strerror(trap_code)); \ + } else { \ + type actual = f; \ + itype iactual; \ + memcpy(&iactual, &actual, sizeof(iactual)); \ + if (is_##kind##_nan_##type(iactual)) { \ + g_tests_passed++; \ + } else { \ + error(__FILE__, __LINE__, \ + "in " #f ": expected result to be a " #kind " nan, got 0x%" fmt \ + ".\n", \ + iactual); \ + } \ + } \ + } while (0) + +#define MULTI_T_UNPACK_(...) __VA_ARGS__ +#define MULTI_T_UNPACK(arg) MULTI_T_UNPACK_ arg +#define MULTI_i8 "%" PRIu8 " " +#define MULTI_i16 "%" PRIu16 " " +#define MULTI_i32 "%u " +#define MULTI_i64 "%" PRIu64 " " +#define MULTI_f32 "%.9g " +#define MULTI_f64 "%.17g " +#define MULTI_str "%s " +#define ASSERT_RETURN_MULTI_T(type, fmt_expected, fmt_got, f, compare, \ + expected, found) \ + do { \ + g_tests_run++; \ + int trap_code = wasm_rt_impl_try(); \ + if (trap_code) { \ + error(__FILE__, __LINE__, #f " trapped (%s).\n", \ + wasm_rt_strerror(trap_code)); \ + } else { \ + type actual = f; \ + if (compare) { \ + g_tests_passed++; \ + } else { \ + error(__FILE__, __LINE__, \ + "in " #f ": expected <" fmt_expected ">, got <" fmt_got ">.\n", \ + MULTI_T_UNPACK(expected), MULTI_T_UNPACK(found)); \ + } \ + } \ + } while (0) + + +#define ASSERT_RETURN_I32(f, expected) ASSERT_RETURN_T(u32, "u", f, expected) +#define ASSERT_RETURN_I64(f, expected) ASSERT_RETURN_T(u64, PRIu64, f, expected) +#define ASSERT_RETURN_F32(f, expected) ASSERT_RETURN_T(f32, ".9g", f, expected) +#define ASSERT_RETURN_F64(f, expected) ASSERT_RETURN_T(f64, ".17g", f, expected) +#define ASSERT_RETURN_EXTERNREF(f, expected) \ + ASSERT_RETURN_T(wasm_rt_externref_t, "p", f, expected); + +#define ASSERT_RETURN_CANONICAL_NAN_F32(f) \ + ASSERT_RETURN_NAN_T(f32, u32, "08x", f, canonical) +#define ASSERT_RETURN_CANONICAL_NAN_F64(f) \ + ASSERT_RETURN_NAN_T(f64, u64, "016x", f, canonical) +#define ASSERT_RETURN_ARITHMETIC_NAN_F32(f) \ + ASSERT_RETURN_NAN_T(f32, u32, "08x", f, arithmetic) +#define ASSERT_RETURN_ARITHMETIC_NAN_F64(f) \ + ASSERT_RETURN_NAN_T(f64, u64, "016x", f, arithmetic) + +static bool is_equal_u8(u8 x, u8 y) { + return x == y; +} + +static bool is_equal_u16(u16 x, u16 y) { + return x == y; +} + +static bool is_equal_u32(u32 x, u32 y) { + return x == y; +} + +static bool is_equal_u64(u64 x, u64 y) { + return x == y; +} + +#define is_equal_i8 is_equal_u8 +#define is_equal_i16 is_equal_u16 +#define is_equal_i32 is_equal_u32 +#define is_equal_i64 is_equal_u64 + +static bool is_equal_wasm_rt_externref_t(wasm_rt_externref_t x, + wasm_rt_externref_t y) { + return x == y; +} + +static inline bool is_equal_wasm_rt_func_type_t(const wasm_rt_func_type_t a, + const wasm_rt_func_type_t b) { + return (a == b) || (a && b && !memcmp(a, b, 32)); +} + +static bool is_equal_wasm_rt_funcref_t(wasm_rt_funcref_t x, + wasm_rt_funcref_t y) { + return is_equal_wasm_rt_func_type_t(x.func_type, y.func_type) && + (x.func == y.func) && (x.module_instance == y.module_instance); +} + +#ifdef WASM_EXN_MAX_SIZE +static bool is_equal_wasm_rt_exnref_t(wasm_rt_exnref_t x, wasm_rt_exnref_t y) { + return x.tag == y.tag && x.size == y.size && !memcmp(x.data, y.data, x.size); +} +#endif + +wasm_rt_externref_t spectest_make_externref(uintptr_t x) { + return (wasm_rt_externref_t)(x + 1); // externref(0) is not null +} + +static u32 f32_bits(f32 x) { + u32 ux; + memcpy(&ux, &x, sizeof(ux)); + return ux; +} + +static u64 f64_bits(f64 x) { + u64 ux; + memcpy(&ux, &x, sizeof(ux)); + return ux; +} + +static bool is_equal_f32(f32 x, f32 y) { + return f32_bits(x) == f32_bits(y); +} + +static bool is_equal_f64(f64 x, f64 y) { + return f64_bits(x) == f64_bits(y); +} + +static f32 make_nan_f32(u32 x) { + x |= 0x7f800000; + f32 res; + memcpy(&res, &x, sizeof(res)); + return res; +} + +static f64 make_nan_f64(u64 x) { + x |= 0x7ff0000000000000; + f64 res; + memcpy(&res, &x, sizeof(res)); + return res; +} + +static bool is_canonical_nan_f32(u32 x) { + return (x & 0x7fffffff) == 0x7fc00000; +} + +static bool is_canonical_nan_f64(u64 x) { + return (x & 0x7fffffffffffffff) == 0x7ff8000000000000; +} + +static bool is_arithmetic_nan_f32(u32 x) { + return (x & 0x7fc00000) == 0x7fc00000; +} + +static bool is_arithmetic_nan_f64(u64 x) { + return (x & 0x7ff8000000000000) == 0x7ff8000000000000; +} + +typedef struct w2c_spectest { + wasm_rt_funcref_table_t spectest_table; + wasm_rt_funcref_table_t spectest_table64; + wasm_rt_memory_t spectest_memory; + uint32_t spectest_global_i32; + uint64_t spectest_global_i64; + float spectest_global_f32; + double spectest_global_f64; +} w2c_spectest; + +static w2c_spectest spectest_instance; + +/* + * spectest implementations + */ +void w2c_spectest_print(w2c_spectest* instance) { + printf("spectest.print()\n"); +} + +void w2c_spectest_print_i32(w2c_spectest* instance, uint32_t i) { + printf("spectest.print_i32(%d)\n", i); +} + +void w2c_spectest_print_i64(w2c_spectest* instance, uint64_t i) { + printf("spectest.print_i64(%" PRIu64 ")\n", i); +} + +void w2c_spectest_print_f32(w2c_spectest* instance, float f) { + printf("spectest.print_f32(%g)\n", f); +} + +void w2c_spectest_print_i32_f32(w2c_spectest* instance, uint32_t i, float f) { + printf("spectest.print_i32_f32(%d %g)\n", i, f); +} + +void w2c_spectest_print_f64(w2c_spectest* instance, double d) { + printf("spectest.print_f64(%g)\n", d); +} + +void w2c_spectest_print_f64_f64(w2c_spectest* instance, double d1, double d2) { + printf("spectest.print_f64_f64(%g %g)\n", d1, d2); +} + +wasm_rt_funcref_table_t* w2c_spectest_table(w2c_spectest* instance) { + return &instance->spectest_table; +} + +wasm_rt_funcref_table_t* w2c_spectest_table64(w2c_spectest* instance) { + return &instance->spectest_table64; +} + +wasm_rt_memory_t* w2c_spectest_memory(w2c_spectest* instance) { + return &instance->spectest_memory; +} + +uint32_t* w2c_spectest_global_i32(w2c_spectest* instance) { + return &instance->spectest_global_i32; +} + +uint64_t* w2c_spectest_global_i64(w2c_spectest* instance) { + return &instance->spectest_global_i64; +} + +float* w2c_spectest_global_f32(w2c_spectest* instance) { + return &instance->spectest_global_f32; +} + +double* w2c_spectest_global_f64(w2c_spectest* instance) { + return &instance->spectest_global_f64; +} + +static void init_spectest_module(w2c_spectest* instance) { + instance->spectest_global_i32 = 666; + 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_DEFAULT_PAGE_SIZE); + wasm_rt_allocate_funcref_table(&instance->spectest_table, 10, 20); + wasm_rt_allocate_funcref_table(&instance->spectest_table64, 10, 20); +} + +// POSIX-only test config where embedder handles signals instead of w2c runtime +#ifdef WASM2C_TEST_EMBEDDER_SIGNAL_HANDLING +#include + +static void posix_signal_handler(int sig, siginfo_t* si, void* unused) { + wasm_rt_trap((si->si_code == SEGV_ACCERR) ? WASM_RT_TRAP_OOB + : WASM_RT_TRAP_EXHAUSTION); +} + +static void posix_install_signal_handler(void) { + /* install altstack */ + stack_t ss; + ss.ss_sp = malloc(SIGSTKSZ); + ss.ss_flags = 0; + ss.ss_size = SIGSTKSZ; + if (sigaltstack(&ss, NULL) != 0) { + perror("sigaltstack failed"); + abort(); + } + + /* install signal handler */ + struct sigaction sa; + memset(&sa, '\0', sizeof(sa)); + sa.sa_flags = SA_SIGINFO | SA_ONSTACK; + sigemptyset(&sa.sa_mask); + sa.sa_sigaction = posix_signal_handler; + if (sigaction(SIGSEGV, &sa, NULL) != 0 || sigaction(SIGBUS, &sa, NULL) != 0) { + perror("sigaction failed"); + abort(); + } +} + +static void posix_cleanup_signal_handler(void) { + /* remove signal handler */ + struct sigaction sa; + memset(&sa, '\0', sizeof(sa)); + sa.sa_handler = SIG_DFL; + if (sigaction(SIGSEGV, &sa, NULL) != 0 || sigaction(SIGBUS, &sa, NULL)) { + perror("sigaction failed"); + abort(); + } + + /* disable and free altstack */ + stack_t ss; + ss.ss_flags = SS_DISABLE; + if (sigaltstack(&ss, NULL) != 0) { + perror("sigaltstack failed"); + abort(); + } + free(ss.ss_sp); +} +#endif + +int main(int argc, char** argv) { +#ifdef WASM2C_TEST_EMBEDDER_SIGNAL_HANDLING + posix_install_signal_handler(); +#endif + wasm_rt_init(); + init_spectest_module(&spectest_instance); + run_spec_tests(); + printf("%u/%u tests passed.\n", g_tests_passed, g_tests_run); + wasm_rt_free(); +#ifdef WASM2C_TEST_EMBEDDER_SIGNAL_HANDLING + posix_cleanup_signal_handler(); +#endif + return g_tests_passed != g_tests_run; +} diff --git a/src/tools/wasm2c/wasm-rt/wasm-rt-exceptions-impl.c b/src/tools/wasm2c/wasm-rt/wasm-rt-exceptions-impl.c new file mode 100644 index 00000000000..73e97201c50 --- /dev/null +++ b/src/tools/wasm2c/wasm-rt/wasm-rt-exceptions-impl.c @@ -0,0 +1,73 @@ +/* + * Copyright 2018 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wasm-rt.h" + +#include "wasm-rt-exceptions.h" + +#include + +#define MAX_EXCEPTION_SIZE 256 + +static WASM_RT_THREAD_LOCAL wasm_rt_tag_t g_active_exception_tag; +static WASM_RT_THREAD_LOCAL uint8_t g_active_exception[MAX_EXCEPTION_SIZE]; +static WASM_RT_THREAD_LOCAL uint32_t g_active_exception_size; + +static WASM_RT_THREAD_LOCAL wasm_rt_jmp_buf* g_unwind_target; + +void wasm_rt_load_exception(const wasm_rt_tag_t tag, + uint32_t size, + const void* values) { + if (size > MAX_EXCEPTION_SIZE) { + wasm_rt_trap(WASM_RT_TRAP_EXHAUSTION); + } + + g_active_exception_tag = tag; + g_active_exception_size = size; + + if (size) { + memcpy(g_active_exception, values, size); + } +} + +WASM_RT_NO_RETURN void wasm_rt_throw(void) { + WASM_RT_LONGJMP(*g_unwind_target, WASM_RT_TRAP_UNCAUGHT_EXCEPTION); +} + +WASM_RT_UNWIND_TARGET* wasm_rt_get_unwind_target(void) { + return g_unwind_target; +} + +void wasm_rt_set_unwind_target(WASM_RT_UNWIND_TARGET* target) { + g_unwind_target = target; +} + +wasm_rt_tag_t wasm_rt_exception_tag(void) { + return g_active_exception_tag; +} + +uint32_t wasm_rt_exception_size(void) { + return g_active_exception_size; +} + +void* wasm_rt_exception(void) { + return g_active_exception; +} + +// Include table operations for exnref +#define WASM_RT_TABLE_OPS_EXNREF +#include "wasm-rt-impl-tableops.inc" +#undef WASM_RT_TABLE_OPS_EXNREF diff --git a/src/tools/wasm2c/wasm-rt/wasm-rt-exceptions.h b/src/tools/wasm2c/wasm-rt/wasm-rt-exceptions.h new file mode 100644 index 00000000000..9e95c196e74 --- /dev/null +++ b/src/tools/wasm2c/wasm-rt/wasm-rt-exceptions.h @@ -0,0 +1,140 @@ +/* + * Copyright 2018 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WASM_RT_EXCEPTIONS_H_ +#define WASM_RT_EXCEPTIONS_H_ + +#include "wasm-rt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * A tag is represented as an arbitrary pointer. + */ +typedef const void* wasm_rt_tag_t; + +/** + * Set the active exception to given tag, size, and contents. + */ +void wasm_rt_load_exception(const wasm_rt_tag_t tag, + uint32_t size, + const void* values); + +/** + * Throw the active exception. + */ +WASM_RT_NO_RETURN void wasm_rt_throw(void); + +/** + * The type of an unwind target if an exception is thrown and caught. + */ +#define WASM_RT_UNWIND_TARGET wasm_rt_jmp_buf + +/** + * Get the current unwind target if an exception is thrown. + */ +WASM_RT_UNWIND_TARGET* wasm_rt_get_unwind_target(void); + +/** + * Set the unwind target if an exception is thrown. + */ +void wasm_rt_set_unwind_target(WASM_RT_UNWIND_TARGET* target); + +/** + * Tag of the active exception. + */ +wasm_rt_tag_t wasm_rt_exception_tag(void); + +/** + * Size of the active exception. + */ +uint32_t wasm_rt_exception_size(void); + +/** + * Contents of the active exception. + */ +void* wasm_rt_exception(void); + +/** + * The maximum size of an exception. + */ +#define WASM_EXN_MAX_SIZE 256 + +/** + * An exception instance (the runtime representation of a function). + * These can be stored in tables of type exnref, or used as values. + */ +typedef struct { + /** The exceptions's tag. */ + wasm_rt_tag_t tag; + /** The size of the exception. */ + uint32_t size; + /** + * The actual contents of the exception are stored inline. + */ + char data[WASM_EXN_MAX_SIZE]; +} wasm_rt_exnref_t; + +/** Default (null) value of an exnref */ +#define wasm_rt_exnref_null_value ((wasm_rt_exnref_t){NULL, 0, {0}}) + +/** A Table of type exnref. */ +typedef struct { + /** The table element data, with an element count of `size`. */ + wasm_rt_exnref_t* data; + /** + * The maximum element count of this Table object. If there is no maximum, + * `max_size` is 0xffffffffu (i.e. UINT32_MAX). + */ + uint32_t max_size; + /** The current element count of the table. */ + uint32_t size; +} wasm_rt_exnref_table_t; + +/** + * Initialize an exnref Table object with an element count of `elements` and a + * maximum size of `max_elements`. + * + * ``` + * wasm_rt_exnref_table_t my_table; + * // 5 elements and a maximum of 10 elements. + * wasm_rt_allocate_exnref_table(&my_table, 5, 10); + * ``` + */ +void wasm_rt_allocate_exnref_table(wasm_rt_exnref_table_t*, + uint32_t elements, + uint32_t max_elements); + +/** Free an exnref Table object. */ +void wasm_rt_free_exnref_table(wasm_rt_exnref_table_t*); + +/** + * Grow a Table object by `delta` elements (giving the new elements the value + * `init`), and return the previous element count. If this new element count is + * greater than the maximum element count, the grow fails and 0xffffffffu + * (UINT32_MAX) is returned instead. + */ +uint32_t wasm_rt_grow_exnref_table(wasm_rt_exnref_table_t*, + uint32_t delta, + wasm_rt_exnref_t init); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/tools/wasm2c/wasm-rt/wasm-rt-impl-tableops.inc b/src/tools/wasm2c/wasm-rt/wasm-rt-impl-tableops.inc new file mode 100644 index 00000000000..76193028725 --- /dev/null +++ b/src/tools/wasm2c/wasm-rt/wasm-rt-impl-tableops.inc @@ -0,0 +1,87 @@ +/* + * Copyright 2018 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This file is used as a template to generate code for table operations for +// funcref or externref. For this, the file must be included after defining +// either WASM_RT_TABLE_OPS_FUNCREF or WASM_RT_TABLE_OPS_EXTERNREF + +#if defined(WASM_RT_TABLE_OPS_FUNCREF) + \ + defined(WASM_RT_TABLE_OPS_EXTERNREF) + \ + defined(WASM_RT_TABLE_OPS_EXNREF) > \ + 1 +#error \ + "Expected only one of { WASM_RT_TABLE_OPS_FUNCREF, WASM_RT_TABLE_OPS_EXTERNREF, WASM_RT_TABLE_OPS_EXNREF } to be defined" +#elif defined(WASM_RT_TABLE_OPS_FUNCREF) + \ + defined(WASM_RT_TABLE_OPS_EXTERNREF) + \ + defined(WASM_RT_TABLE_OPS_EXNREF) < \ + 1 +#error \ + "Expected one of { WASM_RT_TABLE_OPS_FUNCREF, WASM_RT_TABLE_OPS_EXTERNREF, WASM_RT_TABLE_OPS_EXNREF } to be defined" +#endif + +#if defined(WASM_RT_TABLE_OPS_FUNCREF) +#define WASM_RT_TABLE_TYPE wasm_rt_funcref_table_t +#define WASM_RT_TABLE_ELEMENT_TYPE wasm_rt_funcref_t +#define WASM_RT_TABLE_APINAME(name) name##_funcref_table +#elif defined(WASM_RT_TABLE_OPS_EXTERNREF) +#define WASM_RT_TABLE_TYPE wasm_rt_externref_table_t +#define WASM_RT_TABLE_ELEMENT_TYPE wasm_rt_externref_t +#define WASM_RT_TABLE_APINAME(name) name##_externref_table +#elif defined(WASM_RT_TABLE_OPS_EXNREF) +#define WASM_RT_TABLE_TYPE wasm_rt_exnref_table_t +#define WASM_RT_TABLE_ELEMENT_TYPE wasm_rt_exnref_t +#define WASM_RT_TABLE_APINAME(name) name##_exnref_table +#endif + +void WASM_RT_TABLE_APINAME(wasm_rt_allocate)(WASM_RT_TABLE_TYPE* table, + uint32_t elements, + uint32_t max_elements) { + table->size = elements; + table->max_size = max_elements; + table->data = calloc(table->size, sizeof(WASM_RT_TABLE_ELEMENT_TYPE)); +} + +void WASM_RT_TABLE_APINAME(wasm_rt_free)(WASM_RT_TABLE_TYPE* table) { + free(table->data); +} + +uint32_t WASM_RT_TABLE_APINAME(wasm_rt_grow)(WASM_RT_TABLE_TYPE* table, + uint32_t delta, + WASM_RT_TABLE_ELEMENT_TYPE init) { + uint32_t old_elems = table->size; + uint64_t new_elems = (uint64_t)table->size + delta; + if (new_elems == 0) { + return 0; + } + if ((new_elems < old_elems) || (new_elems > table->max_size)) { + return (uint32_t)-1; + } + void* new_data = + realloc(table->data, new_elems * sizeof(WASM_RT_TABLE_ELEMENT_TYPE)); + if (!new_data) { + return (uint32_t)-1; + } + table->data = new_data; + table->size = new_elems; + for (uint32_t i = old_elems; i < new_elems; i++) { + table->data[i] = init; + } + return old_elems; +} + +#undef WASM_RT_TABLE_APINAME +#undef WASM_RT_TABLE_ELEMENT_TYPE +#undef WASM_RT_TABLE_TYPE diff --git a/src/tools/wasm2c/wasm-rt/wasm-rt-impl.c b/src/tools/wasm2c/wasm-rt/wasm-rt-impl.c new file mode 100644 index 00000000000..0cf0723a95d --- /dev/null +++ b/src/tools/wasm2c/wasm-rt/wasm-rt-impl.c @@ -0,0 +1,401 @@ +/* + * Copyright 2018 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wasm-rt-impl.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#if WASM_RT_INSTALL_SIGNAL_HANDLER && !defined(_WIN32) +#include +#include +#endif + +#ifdef _WIN32 +#include +#else +#include +#endif + +#ifndef NDEBUG +#define DEBUG_PRINTF(...) fprintf(stderr, __VA_ARGS__); +#else +#define DEBUG_PRINTF(...) +#endif + +#if WASM_RT_INSTALL_SIGNAL_HANDLER +static bool g_signal_handler_installed = false; +#ifdef _WIN32 +static void* g_sig_handler_handle = 0; +#endif +#endif + +#if WASM_RT_USE_SEGUE +bool wasm_rt_fsgsbase_inst_supported = false; +#ifdef __linux__ +#include +#ifdef __GLIBC__ +#include +#endif +#include // For ARCH_SET_GS +#include // For SYS_arch_prctl +#include // For syscall +#ifndef HWCAP2_FSGSBASE +#define HWCAP2_FSGSBASE (1 << 1) +#endif +#elif defined(__FreeBSD__) +#include // For amd64_set_gsbase etc. +#endif +#endif + +#if WASM_RT_SEGUE_FREE_SEGMENT +WASM_RT_THREAD_LOCAL void* wasm_rt_last_segment_val = NULL; +#endif + +#if WASM_RT_STACK_DEPTH_COUNT +WASM_RT_THREAD_LOCAL uint32_t wasm_rt_call_stack_depth; +WASM_RT_THREAD_LOCAL uint32_t wasm_rt_saved_call_stack_depth; +#elif WASM_RT_STACK_EXHAUSTION_HANDLER +static WASM_RT_THREAD_LOCAL void* g_alt_stack = NULL; +#endif + +#ifndef WASM_RT_TRAP_HANDLER +WASM_RT_THREAD_LOCAL wasm_rt_jmp_buf g_wasm_rt_jmp_buf; +#endif + +#ifdef WASM_RT_TRAP_HANDLER +extern void WASM_RT_TRAP_HANDLER(wasm_rt_trap_t code); +#endif + +void wasm_rt_trap(wasm_rt_trap_t code) { + assert(code != WASM_RT_TRAP_NONE); +#if WASM_RT_STACK_DEPTH_COUNT + wasm_rt_call_stack_depth = wasm_rt_saved_call_stack_depth; +#endif + +#ifdef WASM_RT_TRAP_HANDLER + WASM_RT_TRAP_HANDLER(code); + wasm_rt_unreachable(); +#else + WASM_RT_LONGJMP(g_wasm_rt_jmp_buf, code); +#endif +} + +#ifdef _WIN32 + +#if WASM_RT_INSTALL_SIGNAL_HANDLER + +static LONG os_signal_handler(PEXCEPTION_POINTERS info) { + if (info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { + wasm_rt_trap(WASM_RT_TRAP_OOB); + } else if (info->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) { + wasm_rt_trap(WASM_RT_TRAP_EXHAUSTION); + } + return EXCEPTION_CONTINUE_SEARCH; +} + +static void os_install_signal_handler(void) { + g_sig_handler_handle = + AddVectoredExceptionHandler(1 /* CALL_FIRST */, os_signal_handler); +} + +static void os_cleanup_signal_handler(void) { + RemoveVectoredExceptionHandler(g_sig_handler_handle); +} + +#endif + +#else + +#if WASM_RT_INSTALL_SIGNAL_HANDLER +static void os_signal_handler(int sig, siginfo_t* si, void* unused) { + if (si->si_code == SEGV_ACCERR) { + wasm_rt_trap(WASM_RT_TRAP_OOB); + } else { + wasm_rt_trap(WASM_RT_TRAP_EXHAUSTION); + } +} + +static void os_install_signal_handler(void) { + struct sigaction sa; + memset(&sa, '\0', sizeof(sa)); + sa.sa_flags = SA_SIGINFO; +#if WASM_RT_STACK_EXHAUSTION_HANDLER + sa.sa_flags |= SA_ONSTACK; +#endif + sigemptyset(&sa.sa_mask); + sa.sa_sigaction = os_signal_handler; + + /* Install SIGSEGV and SIGBUS handlers, since macOS seems to use SIGBUS. */ + if (sigaction(SIGSEGV, &sa, NULL) != 0 || sigaction(SIGBUS, &sa, NULL) != 0) { + perror("sigaction failed"); + abort(); + } +} + +static void os_cleanup_signal_handler(void) { + /* Undo what was done in os_install_signal_handler */ + struct sigaction sa; + memset(&sa, '\0', sizeof(sa)); + sa.sa_handler = SIG_DFL; + if (sigaction(SIGSEGV, &sa, NULL) != 0 || sigaction(SIGBUS, &sa, NULL)) { + perror("sigaction failed"); + abort(); + } +} +#endif + +#if WASM_RT_STACK_EXHAUSTION_HANDLER +static bool os_has_altstack_installed() { + /* check for altstack already in place */ + stack_t ss; + if (sigaltstack(NULL, &ss) != 0) { + perror("os_has_altstack_installed: sigaltstack failed"); + abort(); + } + + return !(ss.ss_flags & SS_DISABLE); +} + +/* These routines set up an altstack to handle SIGSEGV from stack overflow. */ +static void os_allocate_and_install_altstack(void) { + /* verify altstack not already allocated */ + assert(!g_alt_stack && + "wasm-rt error: tried to re-allocate thread-local alternate stack"); + + /* We could check and warn if an altstack is already installed, but some + * sanitizers install their own altstack, so this warning would fire + * spuriously and break the test outputs. */ + + /* allocate altstack */ + g_alt_stack = malloc(SIGSTKSZ); + if (g_alt_stack == NULL) { + perror("malloc failed"); + abort(); + } + + /* install altstack */ + stack_t ss; + ss.ss_sp = g_alt_stack; + ss.ss_flags = 0; + ss.ss_size = SIGSTKSZ; + if (sigaltstack(&ss, NULL) != 0) { + perror("os_allocate_and_install_altstack: sigaltstack failed"); + abort(); + } +} + +static void os_disable_and_deallocate_altstack(void) { + /* in debug build, verify altstack allocated */ + assert(g_alt_stack && + "wasm-rt error: thread-local alternate stack not allocated"); + + /* verify altstack was still in place */ + stack_t ss; + if (sigaltstack(NULL, &ss) != 0) { + perror("os_disable_and_deallocate_altstack: sigaltstack failed"); + abort(); + } + + if ((!g_alt_stack) || (ss.ss_flags & SS_DISABLE) || + (ss.ss_sp != g_alt_stack) || (ss.ss_size != SIGSTKSZ)) { + DEBUG_PRINTF( + "wasm-rt warning: alternate stack was modified unexpectedly\n"); + return; + } + + assert(!(ss.ss_flags & SS_ONSTACK) && + "attempt to deallocate altstack while in use"); + + /* disable and free */ + ss.ss_flags = SS_DISABLE; + if (sigaltstack(&ss, NULL) != 0) { + perror("sigaltstack disable failed"); + abort(); + } + assert(!os_has_altstack_installed()); + free(g_alt_stack); + g_alt_stack = NULL; +} +#endif + +#endif + +#if WASM_RT_USE_SEGUE && defined(__FreeBSD__) +static void call_cpuid(uint64_t* rax, + uint64_t* rbx, + uint64_t* rcx, + uint64_t* rdx) { + __asm__ volatile( + "cpuid" + : "=a"(*rax), "=b"(*rbx), "=c"(*rcx), "=d"(*rdx) // output operands + : "a"(*rax), "c"(*rcx) // input operands + ); +} +#endif + +void wasm_rt_init(void) { + wasm_rt_init_thread(); +#if WASM_RT_INSTALL_SIGNAL_HANDLER + if (!g_signal_handler_installed) { + g_signal_handler_installed = true; + os_install_signal_handler(); + } +#endif + +#if WASM_RT_USE_SEGUE +#if defined(__linux__) && defined(__GLIBC__) && __GLIBC__ >= 2 && \ + __GLIBC_MINOR__ >= 18 + // Check for support for userspace wrgsbase instructions + unsigned long val = getauxval(AT_HWCAP2); + wasm_rt_fsgsbase_inst_supported = val & HWCAP2_FSGSBASE; +#elif defined(__FreeBSD__) + // FreeBSD enables fsgsbase on the newer kernels if the hardware supports it. + // We just need to check if the hardware supports it by querying the correct + // cpuid leaf. + uint64_t rax, rbx, rcx, rdx; + rax = 0; + call_cpuid(&rax, &rbx, &rcx, &rdx); + + if (rax > 7) { + rax = 7; + rcx = 0; + call_cpuid(&rax, &rbx, &rcx, &rdx); + if (rbx & 0x1) { + wasm_rt_fsgsbase_inst_supported = true; + } + } +#endif +#endif + + assert(wasm_rt_is_initialized()); +} + +bool wasm_rt_is_initialized(void) { +#if WASM_RT_STACK_EXHAUSTION_HANDLER + if (!os_has_altstack_installed()) { + return false; + } +#endif +#if WASM_RT_INSTALL_SIGNAL_HANDLER + return g_signal_handler_installed; +#else + return true; +#endif +} + +void wasm_rt_free(void) { + assert(wasm_rt_is_initialized()); +#if WASM_RT_INSTALL_SIGNAL_HANDLER + os_cleanup_signal_handler(); + g_signal_handler_installed = false; +#endif + wasm_rt_free_thread(); +} + +void wasm_rt_init_thread(void) { +#if WASM_RT_STACK_EXHAUSTION_HANDLER + os_allocate_and_install_altstack(); +#endif +} + +void wasm_rt_free_thread(void) { +#if WASM_RT_STACK_EXHAUSTION_HANDLER + os_disable_and_deallocate_altstack(); +#endif +} + +#if WASM_RT_USE_SEGUE +void wasm_rt_syscall_set_segue_base(void* base) { + int error_code = 0; +#ifdef __linux__ + error_code = syscall(SYS_arch_prctl, ARCH_SET_GS, base); +#elif defined(__FreeBSD__) + error_code = amd64_set_gsbase(base); +#else +#error "Unknown platform" +#endif + if (error_code != 0) { + perror("wasm_rt_syscall_set_segue_base error"); + abort(); + } +} +void* wasm_rt_syscall_get_segue_base() { + void* base; + int error_code = 0; +#ifdef __linux__ + error_code = syscall(SYS_arch_prctl, ARCH_GET_GS, &base); +#elif defined(__FreeBSD__) + error_code = amd64_get_gsbase(&base); +#else +#error "Unknown platform" +#endif + if (error_code != 0) { + perror("wasm_rt_syscall_get_segue_base error"); + abort(); + } + return base; +} +#endif + +// Include table operations for funcref +#define WASM_RT_TABLE_OPS_FUNCREF +#include "wasm-rt-impl-tableops.inc" +#undef WASM_RT_TABLE_OPS_FUNCREF + +// Include table operations for externref +#define WASM_RT_TABLE_OPS_EXTERNREF +#include "wasm-rt-impl-tableops.inc" +#undef WASM_RT_TABLE_OPS_EXTERNREF + +const char* wasm_rt_strerror(wasm_rt_trap_t trap) { + switch (trap) { + case WASM_RT_TRAP_NONE: + return "No error"; + case WASM_RT_TRAP_OOB: +#if WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS + return "Out-of-bounds access in linear memory or a table, or call stack " + "exhausted"; +#else + return "Out-of-bounds access in linear memory or a table"; + case WASM_RT_TRAP_EXHAUSTION: + return "Call stack exhausted"; +#endif + case WASM_RT_TRAP_INT_OVERFLOW: + return "Integer overflow on divide or truncation"; + case WASM_RT_TRAP_DIV_BY_ZERO: + return "Integer divide by zero"; + case WASM_RT_TRAP_INVALID_CONVERSION: + return "Conversion from NaN to integer"; + case WASM_RT_TRAP_UNREACHABLE: + return "Unreachable instruction executed"; + case WASM_RT_TRAP_CALL_INDIRECT: + return "Invalid call_indirect or return_call_indirect"; + case WASM_RT_TRAP_UNCAUGHT_EXCEPTION: + return "Uncaught exception"; + case WASM_RT_TRAP_UNALIGNED: + return "Unaligned atomic memory access"; + case WASM_RT_TRAP_NULL_REF: + return "Null reference"; + } + return "invalid trap code"; +} diff --git a/src/tools/wasm2c/wasm-rt/wasm-rt-impl.h b/src/tools/wasm2c/wasm-rt/wasm-rt-impl.h new file mode 100644 index 00000000000..ed46b097211 --- /dev/null +++ b/src/tools/wasm2c/wasm-rt/wasm-rt-impl.h @@ -0,0 +1,68 @@ +/* + * Copyright 2018 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WASM_RT_IMPL_H_ +#define WASM_RT_IMPL_H_ + +#include "wasm-rt.h" + +#ifdef _WIN32 +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef WASM_RT_TRAP_HANDLER +/** A setjmp buffer used for handling traps. */ +extern WASM_RT_THREAD_LOCAL wasm_rt_jmp_buf g_wasm_rt_jmp_buf; +#endif + +#if WASM_RT_STACK_DEPTH_COUNT +/** Saved call stack depth that will be restored in case a trap occurs. */ +extern WASM_RT_THREAD_LOCAL uint32_t wasm_rt_saved_call_stack_depth; +#define WASM_RT_SAVE_STACK_DEPTH() \ + wasm_rt_saved_call_stack_depth = wasm_rt_call_stack_depth +#else +#define WASM_RT_SAVE_STACK_DEPTH() (void)0 +#endif + +/** + * Convenience macro to use before calling a wasm function. On first execution + * it will return `WASM_RT_TRAP_NONE` (i.e. 0). If the function traps, it will + * jump back and return the trap that occurred. + * + * ``` + * wasm_rt_trap_t code = wasm_rt_impl_try(); + * if (code != 0) { + * printf("A trap occurred with code: %d\n", code); + * ... + * } + * + * // Call the potentially-trapping function. + * my_wasm_func(); + * ``` + */ +#define wasm_rt_impl_try() \ + (WASM_RT_SAVE_STACK_DEPTH(), wasm_rt_set_unwind_target(&g_wasm_rt_jmp_buf), \ + WASM_RT_SETJMP(g_wasm_rt_jmp_buf)) + +#ifdef __cplusplus +} +#endif + +#endif /* WASM_RT_IMPL_H_ */ diff --git a/src/tools/wasm2c/wasm-rt/wasm-rt-mem-impl-helper.inc b/src/tools/wasm2c/wasm-rt/wasm-rt-mem-impl-helper.inc new file mode 100644 index 00000000000..30a92d3c9df --- /dev/null +++ b/src/tools/wasm2c/wasm-rt/wasm-rt-mem-impl-helper.inc @@ -0,0 +1,197 @@ +/* + * Copyright 2018 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This file is used as a template to generate code for regular memories or for +// shared memories. For this, the file must be included after defining either +// WASM_RT_MEM_OPS or WASM_RT_MEM_OPS_SHARED. + +#if defined(WASM_RT_MEM_OPS) && defined(WASM_RT_MEM_OPS_SHARED) +#error \ + "Expected only one of { WASM_RT_MEM_OPS, WASM_RT_MEM_OPS_SHARED } to be defined" +#elif !defined(WASM_RT_MEM_OPS) && !defined(WASM_RT_MEM_OPS_SHARED) +#error \ + "Expected one of { WASM_RT_MEM_OPS, WASM_RT_MEM_OPS_SHARED } to be defined" +#endif + +// Shared memory operations are defined only if we have C11 +#if defined(WASM_RT_MEM_OPS) || \ + (defined(WASM_RT_MEM_OPS_SHARED) && defined(WASM_RT_C11_AVAILABLE)) + +#ifdef WASM_RT_MEM_OPS + +// Memory operations on wasm_rt_memory_t +#define MEMORY_TYPE wasm_rt_memory_t +#define MEMORY_API_NAME(name) name +#define MEMORY_CELL_TYPE uint8_t* +#define MEMORY_LOCK_VAR_INIT(name) +#define MEMORY_LOCK_AQUIRE(name) +#define MEMORY_LOCK_RELEASE(name) + +#else + +// Memory operations on wasm_rt_shared_memory_t +#define MEMORY_TYPE wasm_rt_shared_memory_t +#define MEMORY_API_NAME(name) name##_shared +#define MEMORY_CELL_TYPE _Atomic volatile uint8_t* + +#if WASM_RT_USE_CRITICALSECTION +#define MEMORY_LOCK_VAR_INIT(name) WIN_MEMORY_LOCK_VAR_INIT(name) +#define MEMORY_LOCK_AQUIRE(name) WIN_MEMORY_LOCK_AQUIRE(name) +#define MEMORY_LOCK_RELEASE(name) WIN_MEMORY_LOCK_RELEASE(name) +#elif WASM_RT_USE_PTHREADS +#define MEMORY_LOCK_VAR_INIT(name) PTHREAD_MEMORY_LOCK_VAR_INIT(name) +#define MEMORY_LOCK_AQUIRE(name) PTHREAD_MEMORY_LOCK_AQUIRE(name) +#define MEMORY_LOCK_RELEASE(name) PTHREAD_MEMORY_LOCK_RELEASE(name) +#endif + +#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, + 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 + if (MEMORY_API_NAME(wasm_rt_memory_is_default32)(memory)) { + const uint64_t mmap_size = + get_alloc_size_for_mmap_default32(memory->max_pages); + uint8_t* addr = os_mmap(mmap_size); + if (!addr) { + os_print_last_error("os_mmap failed."); + abort(); + } + uint8_t* data_end = addr + mmap_size; +#if !WASM_RT_BIG_ENDIAN + int ret = os_mprotect(addr, byte_length); +#else + int ret = os_mprotect(data_end - byte_length, byte_length); +#endif + if (ret != 0) { + os_print_last_error("os_mprotect failed."); + abort(); + } + memory->data = (MEMORY_CELL_TYPE)addr; + memory->data_end = (MEMORY_CELL_TYPE)data_end; + return; + } +#endif + + memory->data = (MEMORY_CELL_TYPE)calloc(byte_length, 1); + memory->data_end = memory->data + byte_length; +} + +// Returns 0 on success +static int MEMORY_API_NAME(expand_data_allocation)(MEMORY_TYPE* memory, + uint64_t old_size, + uint64_t new_size, + uint64_t delta_size) { +#if WASM_RT_USE_MMAP + if (MEMORY_API_NAME(wasm_rt_memory_is_default32)(memory)) { +#if !WASM_RT_BIG_ENDIAN + return os_mprotect((void*)(memory->data + old_size), delta_size); +#else + return os_mprotect((void*)(memory->data_end - old_size - delta_size), + delta_size); +#endif + } +#endif + + uint8_t* new_data = realloc((void*)memory->data, new_size); + if (new_data == NULL) { + return -1; + } +#if !WASM_RT_BIG_ENDIAN + memset((void*)(new_data + old_size), 0, delta_size); +#else + memmove((void*)(new_data + new_size - old_size), (void*)new_data, old_size); + memset((void*)new_data, 0, delta_size); +#endif + + memory->data = (MEMORY_CELL_TYPE)new_data; + memory->data_end = memory->data + new_size; + return 0; +} + +static uint64_t MEMORY_API_NAME(grow_memory_impl)(MEMORY_TYPE* memory, + uint64_t delta) { + uint64_t old_pages = memory->pages; + uint64_t new_pages = memory->pages + delta; + if (new_pages == 0) { + return 0; + } + if (new_pages < old_pages || new_pages > memory->max_pages) { + return (uint64_t)-1; + } + 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; + + int err = MEMORY_API_NAME(expand_data_allocation)(memory, old_size, new_size, + delta_size); + if (err != 0) { + return (uint64_t)-1; + } + + memory->pages = new_pages; + memory->size = new_size; + return old_pages; +} + +uint64_t MEMORY_API_NAME(wasm_rt_grow_memory)(MEMORY_TYPE* memory, + uint64_t delta) { + MEMORY_LOCK_AQUIRE(memory->mem_lock); + uint64_t ret = MEMORY_API_NAME(grow_memory_impl)(memory, delta); + MEMORY_LOCK_RELEASE(memory->mem_lock); +#ifdef WASM_RT_GROW_FAILED_HANDLER + if (ret == (uint64_t)-1) { + WASM_RT_GROW_FAILED_HANDLER(); + } +#endif + return ret; +} + +void MEMORY_API_NAME(wasm_rt_free_memory)(MEMORY_TYPE* memory) { +#if WASM_RT_USE_MMAP + if (MEMORY_API_NAME(wasm_rt_memory_is_default32)(memory)) { + const uint64_t mmap_size = + get_alloc_size_for_mmap_default32(memory->max_pages); + os_munmap((void*)memory->data, mmap_size); // ignore error + return; + } +#endif + free((void*)memory->data); +} + +#undef MEMORY_LOCK_RELEASE +#undef MEMORY_LOCK_AQUIRE +#undef MEMORY_LOCK_VAR_INIT +#undef MEMORY_CELL_TYPE +#undef MEMORY_API_NAME +#undef MEMORY_TYPE + +#endif diff --git a/src/tools/wasm2c/wasm-rt/wasm-rt-mem-impl.c b/src/tools/wasm2c/wasm-rt/wasm-rt-mem-impl.c new file mode 100644 index 00000000000..953c92dbe7a --- /dev/null +++ b/src/tools/wasm2c/wasm-rt/wasm-rt-mem-impl.c @@ -0,0 +1,161 @@ +/* + * Copyright 2018 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wasm-rt-impl.h" + +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +#ifdef WASM_RT_GROW_FAILED_HANDLER +extern void WASM_RT_GROW_FAILED_HANDLER(); +#endif + +#define PTHREAD_MEMORY_LOCK_VAR_INIT(name) \ + if (pthread_mutex_init(&(name), NULL) != 0) { \ + fprintf(stderr, "Lock init failed\n"); \ + abort(); \ + } +#define PTHREAD_MEMORY_LOCK_AQUIRE(name) \ + if (pthread_mutex_lock(&(name)) != 0) { \ + fprintf(stderr, "Lock acquire failed\n"); \ + abort(); \ + } +#define PTHREAD_MEMORY_LOCK_RELEASE(name) \ + if (pthread_mutex_unlock(&(name)) != 0) { \ + fprintf(stderr, "Lock release failed\n"); \ + abort(); \ + } + +#define WIN_MEMORY_LOCK_VAR_INIT(name) InitializeCriticalSection(&(name)) +#define WIN_MEMORY_LOCK_AQUIRE(name) EnterCriticalSection(&(name)) +#define WIN_MEMORY_LOCK_RELEASE(name) LeaveCriticalSection(&(name)) + +#if WASM_RT_USE_MMAP + +#ifdef _WIN32 +static void* os_mmap(size_t size) { + void* ret = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS); + return ret; +} + +static int os_munmap(void* addr, size_t size) { + // Windows can only unmap the whole mapping + (void)size; /* unused */ + BOOL succeeded = VirtualFree(addr, 0, MEM_RELEASE); + return succeeded ? 0 : -1; +} + +static int os_mprotect(void* addr, size_t size) { + if (size == 0) { + return 0; + } + void* ret = VirtualAlloc(addr, size, MEM_COMMIT, PAGE_READWRITE); + if (ret == addr) { + return 0; + } + VirtualFree(addr, 0, MEM_RELEASE); + return -1; +} + +static void os_print_last_error(const char* msg) { + DWORD errorMessageID = GetLastError(); + if (errorMessageID != 0) { + LPSTR messageBuffer = 0; + // The api creates the buffer that holds the message + size_t size = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&messageBuffer, 0, NULL); + (void)size; + printf("%s. %s\n", msg, messageBuffer); + LocalFree(messageBuffer); + } else { + printf("%s. No error code.\n", msg); + } +} + +#else /* !_WIN32 */ +static void* os_mmap(size_t size) { + int map_prot = PROT_NONE; + int map_flags = MAP_ANONYMOUS | MAP_PRIVATE; + uint8_t* addr = mmap(NULL, size, map_prot, map_flags, -1, 0); + if (addr == MAP_FAILED) + return NULL; + return addr; +} + +static int os_munmap(void* addr, size_t size) { + return munmap(addr, size); +} + +static int os_mprotect(void* addr, size_t size) { + if (size == 0) { + return 0; + } + return mprotect(addr, size, PROT_READ | PROT_WRITE); +} + +static void os_print_last_error(const char* msg) { + perror(msg); +} + +#endif /* _WIN32 */ + +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_DEFAULT_PAGE_SIZE; + return max_size; + } + + /* Reserve 4GiB. */ + const uint64_t max_size = 0x100000000ul; + return max_size; +#endif +} + +#endif /* WASM_RT_USE_MMAP */ + +// Include operations for memory +#define WASM_RT_MEM_OPS +#include "wasm-rt-mem-impl-helper.inc" +#undef WASM_RT_MEM_OPS + +// Include operations for shared memory +#define WASM_RT_MEM_OPS_SHARED +#include "wasm-rt-mem-impl-helper.inc" +#undef WASM_RT_MEM_OPS_SHARED + +#undef C11_MEMORY_LOCK_VAR_INIT +#undef C11_MEMORY_LOCK_AQUIRE +#undef C11_MEMORY_LOCK_RELEASE +#undef PTHREAD_MEMORY_LOCK_VAR_INIT +#undef PTHREAD_MEMORY_LOCK_AQUIRE +#undef PTHREAD_MEMORY_LOCK_RELEASE +#undef WIN_MEMORY_LOCK_VAR_INIT +#undef WIN_MEMORY_LOCK_AQUIRE +#undef WIN_MEMORY_LOCK_RELEASE diff --git a/src/tools/wasm2c/wasm-rt/wasm-rt.h b/src/tools/wasm2c/wasm-rt/wasm-rt.h new file mode 100644 index 00000000000..2c9b635f626 --- /dev/null +++ b/src/tools/wasm2c/wasm-rt/wasm-rt.h @@ -0,0 +1,728 @@ +/* + * Copyright 2018 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WASM_RT_H_ +#define WASM_RT_H_ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __has_builtin +#define __has_builtin(x) 0 /** Compatibility with non-clang compilers. */ +#endif + +#if __has_builtin(__builtin_expect) +#define UNLIKELY(x) __builtin_expect(!!(x), 0) +#define LIKELY(x) __builtin_expect(!!(x), 1) +#else +#define UNLIKELY(x) (x) +#define LIKELY(x) (x) +#endif + +#if __has_builtin(__builtin_memcpy) +#define wasm_rt_memcpy __builtin_memcpy +#else +#define wasm_rt_memcpy memcpy +#endif + +#if __has_builtin(__builtin_unreachable) +#define wasm_rt_unreachable __builtin_unreachable +#else +#define wasm_rt_unreachable abort +#endif + +#ifdef __STDC_VERSION__ +#if __STDC_VERSION__ >= 201112L +#define WASM_RT_C11_AVAILABLE +#endif +#endif + +/** + * Many devices don't implement the C11 threads.h. We use CriticalSection APIs + * for Windows and pthreads on other platforms where threads are not available. + */ +#ifdef WASM_RT_C11_AVAILABLE + +#if defined(_WIN32) +#include +#define WASM_RT_MUTEX CRITICAL_SECTION +#define WASM_RT_USE_CRITICALSECTION 1 +#else +#include +#define WASM_RT_MUTEX pthread_mutex_t +#define WASM_RT_USE_PTHREADS 1 +#endif + +#endif + +#ifdef WASM_RT_C11_AVAILABLE +#define WASM_RT_THREAD_LOCAL _Thread_local +#elif defined(_MSC_VER) +#define WASM_RT_THREAD_LOCAL __declspec(thread) +#elif (defined(__GNUC__) || defined(__clang__)) && !defined(__APPLE__) +// Disabled on Apple systems due to sporadic test failures. +#define WASM_RT_THREAD_LOCAL __thread +#else +#define WASM_RT_THREAD_LOCAL +#endif + +/** + * If enabled, perform additional sanity checks in the generated wasm2c code and + * wasm2c runtime. This is useful to enable on debug builds. + */ +#ifndef WASM_RT_SANITY_CHECKS +#define WASM_RT_SANITY_CHECKS 0 +#endif + +#ifndef WASM_RT_BIG_ENDIAN +#define WASM_RT_BIG_ENDIAN 0 +#endif + +/** + * Backward compatibility: Convert the previously exposed + * WASM_RT_MEMCHECK_SIGNAL_HANDLER macro to the ALLOCATION and CHECK macros that + * are now used. + */ +#if defined(WASM_RT_MEMCHECK_SIGNAL_HANDLER) + +#if WASM_RT_MEMCHECK_SIGNAL_HANDLER +#define WASM_RT_USE_MMAP 1 +#define WASM_RT_MEMCHECK_GUARD_PAGES 1 +#else +#define WASM_RT_USE_MMAP 0 +#define WASM_RT_MEMCHECK_BOUNDS_CHECK 1 +#endif + +#warning \ + "WASM_RT_MEMCHECK_SIGNAL_HANDLER has been deprecated in favor of WASM_RT_USE_MMAP and WASM_RT_MEMORY_CHECK_* macros" +#endif + +/** + * Specify if we use OR mmap/mprotect (+ Windows equivalents) OR malloc/realloc + * for the Wasm memory allocation and growth. mmap/mprotect guarantees memory + * will grow without being moved, while malloc ensures the virtual memory is + * consumed only as needed, but may relocate the memory to handle memory + * fragmentation. + * + * This defaults to malloc on 32-bit platforms or if memory64 support is needed. + * It defaults to mmap on 64-bit platforms assuming memory64 support is not + * needed (so we can use the guard based range checks below). + */ +#ifndef WASM_RT_USE_MMAP +#if UINTPTR_MAX > 0xffffffff +#define WASM_RT_USE_MMAP 1 +#else +#define WASM_RT_USE_MMAP 0 +#endif +#endif + +/** + * Set the range checking strategy for Wasm memories. + * + * GUARD_PAGES: memory accesses rely on unmapped pages/guard pages to trap + * out-of-bound accesses. + * + * BOUNDS_CHECK: memory accesses are checked with explicit bounds checks. + * + * This defaults to GUARD_PAGES as this is the fastest option, iff the + * requirements of GUARD_PAGES --- 64-bit platforms, MMAP allocation strategy, + * no 64-bit memories --- are met. This falls back to BOUNDS otherwise. + */ + +/** Check if Guard checks are supported */ +#if UINTPTR_MAX > 0xffffffff && WASM_RT_USE_MMAP +#define WASM_RT_GUARD_PAGES_SUPPORTED 1 +#else +#define WASM_RT_GUARD_PAGES_SUPPORTED 0 +#endif + +/** Specify defaults for memory checks if unspecified */ +#if !defined(WASM_RT_MEMCHECK_GUARD_PAGES) && \ + !defined(WASM_RT_MEMCHECK_BOUNDS_CHECK) +#if WASM_RT_GUARD_PAGES_SUPPORTED +#define WASM_RT_MEMCHECK_GUARD_PAGES 1 +#else +#define WASM_RT_MEMCHECK_BOUNDS_CHECK 1 +#endif +#endif + +/** Ensure the macros are defined */ +#ifndef WASM_RT_MEMCHECK_GUARD_PAGES +#define WASM_RT_MEMCHECK_GUARD_PAGES 0 +#endif +#ifndef WASM_RT_MEMCHECK_BOUNDS_CHECK +#define WASM_RT_MEMCHECK_BOUNDS_CHECK 0 +#endif + +/** Sanity check the use of guard pages */ +#if WASM_RT_MEMCHECK_GUARD_PAGES && !WASM_RT_GUARD_PAGES_SUPPORTED +#error \ + "WASM_RT_MEMCHECK_GUARD_PAGES not supported on this platform/configuration" +#endif + +#if WASM_RT_MEMCHECK_GUARD_PAGES && WASM_RT_MEMCHECK_BOUNDS_CHECK +#error \ + "Cannot use both WASM_RT_MEMCHECK_GUARD_PAGES and WASM_RT_MEMCHECK_BOUNDS_CHECK" + +#elif !WASM_RT_MEMCHECK_GUARD_PAGES && !WASM_RT_MEMCHECK_BOUNDS_CHECK +#error \ + "Must choose at least one from WASM_RT_MEMCHECK_GUARD_PAGES and WASM_RT_MEMCHECK_BOUNDS_CHECK" +#endif + +/** + * Some configurations above require the Wasm runtime to install a signal + * handler. However, this can be explicitly disallowed by the host using + * WASM_RT_SKIP_SIGNAL_RECOVERY. In this case, when the wasm code encounters an + * OOB access, it may either trap or abort. + */ +#ifndef WASM_RT_SKIP_SIGNAL_RECOVERY +#define WASM_RT_SKIP_SIGNAL_RECOVERY 0 +#endif + +#if WASM_RT_MEMCHECK_GUARD_PAGES && !WASM_RT_SKIP_SIGNAL_RECOVERY +#define WASM_RT_INSTALL_SIGNAL_HANDLER 1 +#else +#define WASM_RT_INSTALL_SIGNAL_HANDLER 0 +#endif + +/** + * This macro, if defined to 1 (i.e., allows the "segue" optimization), allows + * Wasm2c to use segment registers to speedup access to the linear heap. Note + * that even if allowed in this way, the segment registers would only be used if + * Wasm2c output is compiled for a suitable architecture and OS and the produces + * C file is compiled by supported compilers. The extact restrictions are listed + * in detail in src/template/wasm2c.declarations.c + */ +#ifndef WASM_RT_ALLOW_SEGUE +#define WASM_RT_ALLOW_SEGUE 0 +#endif + +/** + * The segue optimization restores x86 segments to their old values when exiting + * wasm2c code. If WASM_RT_SEGUE_FREE_SEGMENT is defined, wasm2c assumes it has + * exclusive use of the segment and optimizes performance in two ways. First, it + * does not restore the "old" value of the segment during exits. Second, wasm2c + * only sets the segment register if the value has changed since the last time + * it was set. + */ +#ifndef WASM_RT_SEGUE_FREE_SEGMENT +#define WASM_RT_SEGUE_FREE_SEGMENT 0 +#endif + +#ifndef WASM_RT_USE_SEGUE +// Memory functions can use the segue optimization if allowed. The segue +// optimization uses x86 segments to point to a linear memory. We use this +// optimization when: +// +// (1) Segue is allowed using WASM_RT_ALLOW_SEGUE +// (2) on x86_64 without WASM_RT_BIG_ENDIAN enabled +// (3) the compiler supports: intrinsics for (rd|wr)gsbase, "address namespaces" +// for accessing pointers, and supports memcpy on pointers with custom +// "address namespaces". GCC does not support the memcpy requirement, so +// this leaves only clang (version 9 or later) for now. +// (4) The OS provides a way to query if (rd|wr)gsbase is allowed by the kernel +// or the implementation has to use a syscall for this. +// (5) The OS doesn't replace the segment register on context switch which +// eliminates windows for now +// +// While more OS can be supported in the future, we only support linux for now +#if WASM_RT_ALLOW_SEGUE && !WASM_RT_BIG_ENDIAN && \ + (defined(__x86_64__) || defined(_M_X64)) && __clang__ && \ + (__clang_major__ >= 9) && __has_builtin(__builtin_ia32_wrgsbase64) && \ + !defined(_WIN32) && !defined(__ANDROID__) && \ + (defined(__linux__) || defined(__FreeBSD__)) +#define WASM_RT_USE_SEGUE 1 +#else +#define WASM_RT_USE_SEGUE 0 +#endif +#endif + +/** + * This macro, if defined, allows the embedder to disable all stack exhaustion + * checks. This a non conformant configuration, i.e., this does not respect + * Wasm's specification, and may compromise security. Use with caution. + */ +#ifndef WASM_RT_NONCONFORMING_UNCHECKED_STACK_EXHAUSTION +#define WASM_RT_NONCONFORMING_UNCHECKED_STACK_EXHAUSTION 0 +#endif + +/** + * We need to detect and trap stack overflows. If we use a signal handler on + * POSIX systems, this can detect call stack overflows. On windows, or platforms + * without a signal handler, we use stack depth counting. The s390x big endian + * platform additionally seems to have issues with stack guard pages, so we play + * it safe and use stack counting on big endian platforms. + */ +#if !defined(WASM_RT_STACK_DEPTH_COUNT) && \ + !defined(WASM_RT_STACK_EXHAUSTION_HANDLER) && \ + !WASM_RT_NONCONFORMING_UNCHECKED_STACK_EXHAUSTION + +#if WASM_RT_INSTALL_SIGNAL_HANDLER && !defined(_WIN32) && !WASM_RT_BIG_ENDIAN +#define WASM_RT_STACK_EXHAUSTION_HANDLER 1 +#else +#define WASM_RT_STACK_DEPTH_COUNT 1 +#endif + +#endif + +/** Ensure the stack macros are defined */ +#ifndef WASM_RT_STACK_DEPTH_COUNT +#define WASM_RT_STACK_DEPTH_COUNT 0 +#endif +#ifndef WASM_RT_STACK_EXHAUSTION_HANDLER +#define WASM_RT_STACK_EXHAUSTION_HANDLER 0 +#endif + +#if WASM_RT_NONCONFORMING_UNCHECKED_STACK_EXHAUSTION + +#if (WASM_RT_STACK_EXHAUSTION_HANDLER + WASM_RT_STACK_DEPTH_COUNT) != 0 +#error \ + "Cannot specify WASM_RT_NONCONFORMING_UNCHECKED_STACK_EXHAUSTION along with WASM_RT_STACK_EXHAUSTION_HANDLER or WASM_RT_STACK_DEPTH_COUNT" +#endif + +#else + +#if (WASM_RT_STACK_EXHAUSTION_HANDLER + WASM_RT_STACK_DEPTH_COUNT) > 1 +#error \ + "Cannot specify multiple options from WASM_RT_STACK_EXHAUSTION_HANDLER , WASM_RT_STACK_DEPTH_COUNT" +#elif (WASM_RT_STACK_EXHAUSTION_HANDLER + WASM_RT_STACK_DEPTH_COUNT) == 0 +#error \ + "Must specify one of WASM_RT_STACK_EXHAUSTION_HANDLER , WASM_RT_STACK_DEPTH_COUNT" +#endif + +#endif + +#if WASM_RT_STACK_EXHAUSTION_HANDLER && !WASM_RT_INSTALL_SIGNAL_HANDLER +#error \ + "WASM_RT_STACK_EXHAUSTION_HANDLER can only be used if WASM_RT_INSTALL_SIGNAL_HANDLER is enabled" +#endif + +#if WASM_RT_STACK_DEPTH_COUNT +/** + * When the signal handler cannot be used to detect stack overflows, stack depth + * is limited explicitly. The maximum stack depth before trapping can be + * configured by defining this symbol before including wasm-rt when building the + * generated c files, for example: + * + * ``` + * cc -c -DWASM_RT_MAX_CALL_STACK_DEPTH=100 my_module.c -o my_module.o + * ``` + */ +#ifndef WASM_RT_MAX_CALL_STACK_DEPTH +#define WASM_RT_MAX_CALL_STACK_DEPTH 500 +#endif + +/** Current call stack depth. */ +extern WASM_RT_THREAD_LOCAL uint32_t wasm_rt_call_stack_depth; + +#endif + +#if WASM_RT_USE_SEGUE +/** + * The segue optimization uses x86 segments to point to a linear memory. If + * used, the runtime must query whether it can use the fast userspace wrgsbase + * instructions or whether it must invoke syscalls to set the segment base, + * depending on the supported CPU features. The result of this query is saved in + * this variable. + */ +extern bool wasm_rt_fsgsbase_inst_supported; +/** + * If fast userspace wrgsbase instructions don't exist, the runtime most provide + * a function that invokes the OS' underlying syscall to set the segment base. + */ +void wasm_rt_syscall_set_segue_base(void* base); +/** + * If fast userspace rdgsbase instructions don't exist, the runtime most provide + * a function that invokes the OS' underlying syscall to get the segment base. + */ +void* wasm_rt_syscall_get_segue_base(); +/** + * If WASM_RT_SEGUE_FREE_SEGMENT is defined, we must only set the segment + * register if it was changed since the last time it was set. The last value set + * on the segment register is stored in this variable. + */ +#if WASM_RT_SEGUE_FREE_SEGMENT +extern WASM_RT_THREAD_LOCAL void* wasm_rt_last_segment_val; +#endif +#endif + +#if defined(_MSC_VER) +#define WASM_RT_NO_RETURN __declspec(noreturn) +#else +#define WASM_RT_NO_RETURN __attribute__((noreturn)) +#endif + +#if defined(__APPLE__) && WASM_RT_STACK_EXHAUSTION_HANDLER +#define WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS 1 +#else +#define WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS 0 +#endif + +/** Reason a trap occurred. Provide this to `wasm_rt_trap`. */ +typedef enum { + WASM_RT_TRAP_NONE, /** No error. */ + WASM_RT_TRAP_OOB, /** Out-of-bounds access in linear memory or a table. */ + WASM_RT_TRAP_INT_OVERFLOW, /** Integer overflow on divide or truncation. */ + WASM_RT_TRAP_DIV_BY_ZERO, /** Integer divide by zero. */ + WASM_RT_TRAP_INVALID_CONVERSION, /** Conversion from NaN to integer. */ + WASM_RT_TRAP_UNREACHABLE, /** Unreachable instruction executed. */ + WASM_RT_TRAP_CALL_INDIRECT, /** Invalid call_indirect, for any reason. */ + WASM_RT_TRAP_NULL_REF, /** Null reference. */ + WASM_RT_TRAP_UNCAUGHT_EXCEPTION, /** Exception thrown and not caught. */ + WASM_RT_TRAP_UNALIGNED, /** Unaligned atomic instruction executed. */ +#if WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS + WASM_RT_TRAP_EXHAUSTION = WASM_RT_TRAP_OOB, +#else + WASM_RT_TRAP_EXHAUSTION, /** Call stack exhausted. */ +#endif +} wasm_rt_trap_t; + +/** Value types. Used to define function signatures. */ +typedef enum { + WASM_RT_I32, + WASM_RT_I64, + WASM_RT_F32, + WASM_RT_F64, + WASM_RT_V128, + WASM_RT_FUNCREF, + WASM_RT_EXTERNREF, + WASM_RT_EXNREF, +} wasm_rt_type_t; + +/** + * A generic function pointer type, both for Wasm functions (`code`) + * and host functions (`hostcode`). All function pointers are stored + * in this canonical form, but must be cast to their proper signature + * to call. + */ +typedef void (*wasm_rt_function_ptr_t)(void); + +/** + * A pointer to a "tail-callee" function, called by a tail-call + * trampoline or by another tail-callee function. (The definition uses a + * single-member struct to allow a recursive definition.) + */ +typedef struct wasm_rt_tailcallee_t { + void (*fn)(void** instance_ptr, + void* tail_call_stack, + struct wasm_rt_tailcallee_t* next); +} wasm_rt_tailcallee_t; + +/** + * The type of a function (an arbitrary number of param and result types). + * This is represented as an opaque 256-bit ID. + */ +typedef const char* wasm_rt_func_type_t; + +/** + * A function instance (the runtime representation of a function). + * These can be stored in tables of type funcref, or used as values. + */ +typedef struct { + /** The function's type. */ + wasm_rt_func_type_t func_type; + /** + * The function. The embedder must know the actual C signature of the function + * and cast to it before calling. + */ + wasm_rt_function_ptr_t func; + /** An alternate version of the function to be used when tail-called. */ + wasm_rt_tailcallee_t func_tailcallee; + /** + * A function instance is a closure of the function over an instance + * of the originating module. The module_instance element will be passed into + * the function at runtime. + */ + void* module_instance; +} wasm_rt_funcref_t; + +/** Default (null) value of a funcref */ +#define wasm_rt_funcref_null_value \ + ((wasm_rt_funcref_t){NULL, NULL, {NULL}, NULL}) + +/** The type of an external reference (opaque to WebAssembly). */ +typedef void* wasm_rt_externref_t; + +/** Default (null) value of an externref */ +#define wasm_rt_externref_null_value ((wasm_rt_externref_t){NULL}) + +/** A Memory object. */ +typedef struct { + /** The linear memory data, with a byte length of `size`. */ + uint8_t* data; + /** The location after the the reserved space for the linear memory data. */ + uint8_t* data_end; + /** 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. */ + uint64_t max_pages; + /** The current size of the linear memory, in bytes. */ + uint64_t size; + /** Is this memory indexed by u64 (as opposed to default u32) */ + bool is64; +} wasm_rt_memory_t; + +#ifdef WASM_RT_C11_AVAILABLE +/** A shared Memory object. */ +typedef struct { + /** + * The linear memory data, with a byte length of `size`. The memory is marked + * atomic as it is shared and may have to be accessed with different memory + * orders --- sequential when being accessed atomically, relaxed otherwise. + * Unfortunately, the C standard does not state what happens if there are + * overlaps in two memory accesses which have a memory order, e.g., an + * atomic32 being read from the same location an atomic64 is read. One way to + * prevent optimizations from assuming non-overlapping behavior as typically + * done in C is to mark the memory as volatile. Thus the memory is atomic and + * volatile. + */ + _Atomic volatile uint8_t* data; + /** The location one byte after the reserved space for the linear memory data. + * This includes any reserved pages that are not yet allocated. */ + _Atomic volatile uint8_t* data_end; + /** 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. */ + uint64_t max_pages; + /** The current size of the linear memory, in bytes. */ + uint64_t size; + /** Is this memory indexed by u64 (as opposed to default u32) */ + bool is64; + /** Lock used to ensure operations such as memory grow are threadsafe */ + WASM_RT_MUTEX mem_lock; +} wasm_rt_shared_memory_t; +#endif + +/** A Table of type funcref. */ +typedef struct { + /** The table element data, with an element count of `size`. */ + wasm_rt_funcref_t* data; + /** + * The maximum element count of this Table object. If there is no maximum, + * `max_size` is 0xffffffffu (i.e. UINT32_MAX). + */ + uint32_t max_size; + /** The current element count of the table. */ + uint32_t size; +} wasm_rt_funcref_table_t; + +/** A Table of type externref. */ +typedef struct { + /** The table element data, with an element count of `size`. */ + wasm_rt_externref_t* data; + /** + * The maximum element count of this Table object. If there is no maximum, + * `max_size` is 0xffffffffu (i.e. UINT32_MAX). + */ + uint32_t max_size; + /** The current element count of the table. */ + uint32_t size; +} wasm_rt_externref_table_t; + +/** Initialize the runtime. */ +void wasm_rt_init(void); + +/** Is the runtime initialized? */ +bool wasm_rt_is_initialized(void); + +/** Free the runtime's state. */ +void wasm_rt_free(void); + +/* + * Initialize the multithreaded runtime for a given thread. Must be + * called by each thread (other than the one that called wasm_rt_init) + * before initializing a Wasm module or calling an exported + * function. + */ +void wasm_rt_init_thread(void); + +/* + * Free the individual thread's state. + */ +void wasm_rt_free_thread(void); + +/** A hardened jmp_buf that allows checking for initialization before use */ +typedef struct { + /** Is the jmp buf intialized? */ + bool initialized; + /** jmp_buf contents */ + jmp_buf buffer; +} wasm_rt_jmp_buf; + +#ifndef _WIN32 +#define WASM_RT_SETJMP_TRAP_SETBUF(buf) sigsetjmp(buf, 1) + +/** + * On macOS XNU, there is a bug where nested `sigsetjmp` and `siglongjmp` + * across threads that have an allocated alternate signal stack (`SS_ONSTACK`) + * will erroneously cause the kernel to preserve the `SS_ONSTACK` flag in the + * thread state + * + * See: https://github.com/WebAssembly/wabt/issues/2654 + * See: https://github.com/golang/go/issues/44501 + */ +#define WASM_RT_SETJMP_EXN_SETBUF(buf) sigsetjmp(buf, 0) +#else +#define WASM_RT_SETJMP_TRAP_SETBUF(buf) setjmp(buf) +#define WASM_RT_SETJMP_EXN_SETBUF(buf) setjmp(buf) +#endif + +#define WASM_RT_SETJMP(buf) \ + ((buf).initialized = true, WASM_RT_SETJMP_TRAP_SETBUF((buf).buffer)) +#define WASM_RT_SETJMP_EXN(buf) \ + ((buf).initialized = true, WASM_RT_SETJMP_EXN_SETBUF((buf).buffer)) + +#ifndef _WIN32 +#define WASM_RT_LONGJMP_UNCHECKED(buf, val) siglongjmp(buf, val) +#else +#define WASM_RT_LONGJMP_UNCHECKED(buf, val) longjmp(buf, val) +#endif + +#define WASM_RT_LONGJMP(buf, val) \ + /** Abort on failure as this may be called in the trap handler */ \ + if (!((buf).initialized)) \ + abort(); \ + (buf).initialized = false; \ + WASM_RT_LONGJMP_UNCHECKED((buf).buffer, val) + +/** + * Stop execution immediately and jump back to the call to `wasm_rt_impl_try`. + * The result of `wasm_rt_impl_try` will be the provided trap reason. + * + * This is typically called by the generated code, and not the embedder. + */ +WASM_RT_NO_RETURN void wasm_rt_trap(wasm_rt_trap_t); + +/** Return a human readable error string based on a trap type. */ +const char* wasm_rt_strerror(wasm_rt_trap_t trap); + +#define wasm_rt_try(target) WASM_RT_SETJMP_EXN(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. + * + * ``` + * 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_DEFAULT_PAGE_SIZE); + * ``` + */ +void wasm_rt_allocate_memory(wasm_rt_memory_t*, + uint64_t initial_pages, + uint64_t max_pages, + bool is64, + uint32_t page_size); + +/** + * Grow a Memory object by `pages`, and return the previous page count. If + * this new page count is greater than the maximum page count, the grow fails + * and 0xffffffffu (UINT32_MAX) is returned instead. + * + * ``` + * wasm_rt_memory_t my_memory; + * ... + * // Grow memory by 10 pages. + * uint32_t old_page_size = wasm_rt_grow_memory(&my_memory, 10); + * if (old_page_size == UINT32_MAX) { + * // Failed to grow memory. + * } + * ``` + */ +uint64_t wasm_rt_grow_memory(wasm_rt_memory_t*, uint64_t pages); + +/** Free a Memory object. */ +void wasm_rt_free_memory(wasm_rt_memory_t*); + +#ifdef WASM_RT_C11_AVAILABLE +/** Shared memory version of wasm_rt_allocate_memory */ +void wasm_rt_allocate_memory_shared(wasm_rt_shared_memory_t*, + uint64_t initial_pages, + uint64_t max_pages, + 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); + +/** Shared memory version of wasm_rt_free_memory */ +void wasm_rt_free_memory_shared(wasm_rt_shared_memory_t*); +#endif + +/** + * Initialize a funcref Table object with an element count of `elements` and a + * maximum size of `max_elements`. + * + * ``` + * wasm_rt_funcref_table_t my_table; + * // 5 elements and a maximum of 10 elements. + * wasm_rt_allocate_funcref_table(&my_table, 5, 10); + * ``` + */ +void wasm_rt_allocate_funcref_table(wasm_rt_funcref_table_t*, + uint32_t elements, + uint32_t max_elements); + +/** Free a funcref Table object. */ +void wasm_rt_free_funcref_table(wasm_rt_funcref_table_t*); + +/** + * Initialize an externref Table object with an element count + * of `elements` and a maximum size of `max_elements`. + * Usage as per wasm_rt_allocate_funcref_table. + */ +void wasm_rt_allocate_externref_table(wasm_rt_externref_table_t*, + uint32_t elements, + uint32_t max_elements); + +/** Free an externref Table object. */ +void wasm_rt_free_externref_table(wasm_rt_externref_table_t*); + +/** + * Grow a Table object by `delta` elements (giving the new elements the value + * `init`), and return the previous element count. If this new element count is + * greater than the maximum element count, the grow fails and 0xffffffffu + * (UINT32_MAX) is returned instead. + */ +uint32_t wasm_rt_grow_funcref_table(wasm_rt_funcref_table_t*, + uint32_t delta, + wasm_rt_funcref_t init); +uint32_t wasm_rt_grow_externref_table(wasm_rt_externref_table_t*, + uint32_t delta, + wasm_rt_externref_t init); + +#ifdef __cplusplus +} +#endif + +#endif /* WASM_RT_H_ */ diff --git a/src/tools/wasm2c/wasm2c-builder.cpp b/src/tools/wasm2c/wasm2c-builder.cpp new file mode 100644 index 00000000000..9b6b2180bc5 --- /dev/null +++ b/src/tools/wasm2c/wasm2c-builder.cpp @@ -0,0 +1,141 @@ +/* + * Copyright 2026 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "tools/wasm2c/c-printer.h" +#include "tools/wasm2c/wasm2c-builder.h" +#include "wasm.h" + +// code to be inserted into the generated output +extern const char* HeaderTop; +extern const char* HeaderBottom; +extern const char* SourceIncludes; +extern const char* SourceDeclarations; + +namespace wasm { + +namespace { + +std::string mangleName(const std::string& name) { + if (name.empty()) { + return ""; + } + std::string result; + bool isFirst = true; + for (char c : name) { + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9')) { + result += c; + isFirst = false; + } else if (c == '_') { + if (isFirst) { + result += "0x5F"; + isFirst = false; + } else { + result += '_'; + } + } else { + char buf[8]; + snprintf(buf, sizeof(buf), "0x%02X", (unsigned char)c); + result += buf; + isFirst = false; + } + } + return result; +} + +} // anonymous namespace + +void Wasm2CBuilder::processWasm(Module* wasm, + std::ostream& cOut, + std::ostream& hOut) { + this->module = wasm; + std::string moduleName = + flags.moduleName.empty() ? "test" : mangleName(flags.moduleName); + std::string guardName = "WASM_H_GENERATED_" + moduleName; + std::transform( + guardName.begin(), guardName.end(), guardName.begin(), ::toupper); + + CPrinter h(hOut); + CPrinter c(cOut); + + // Header file prefix + h << "/* Automatically generated by wasm2c */" << endl; + h << "#ifndef " << guardName << endl; + h << "#define " << guardName << endl << endl; + h << "#include \"wasm-rt.h\"" << endl << endl; + h << HeaderTop << endl; + + // Source file prefix + c << "/* Automatically generated by wasm2c */" << endl; + c << SourceIncludes << endl; + if (!flags.headerName.empty()) { + c << "#include \"" << flags.headerName << "\"" << endl; + } else { + c << "#include \"wasm.h\"" << endl; + } + c << SourceDeclarations << endl; + + // Structure context definition + h << "typedef struct w2c_" << moduleName << " {" << endl; + h.indent(); + h << "char dummy_member;" << endl; + h.outdent(); + h << "} w2c_" << moduleName << ";" << endl << endl; + + // Lifecycle signatures in header + h << "void wasm2c_" << moduleName << "_instantiate(w2c_" << moduleName << "*"; + h << ");" << endl; + h << "void wasm2c_" << moduleName << "_free(w2c_" << moduleName << "*);" + << endl; + h << "wasm_rt_func_type_t wasm2c_" << moduleName + << "_get_func_type(uint32_t param_count, uint32_t result_count, ...);" + << endl; + + // Instantiate hooks + c << "void wasm2c_" << moduleName << "_instantiate(w2c_" << moduleName + << "* instance"; + c << ") {" << endl; + c.indent(); + c << "assert(wasm_rt_is_initialized());" << endl; + c.outdent(); + c << "}" << endl << endl; + + // Free hooks + c << "void wasm2c_" << moduleName << "_free(w2c_" << moduleName + << "* instance) {" << endl; + c << "}" << endl << endl; + + // Signature match ladders + c << "wasm_rt_func_type_t wasm2c_" << moduleName + << "_get_func_type(uint32_t param_count, uint32_t result_count, ...) {" + << endl; + c.indent(); + c << "va_list args;" << endl << endl; + + c << "return NULL;" << endl; + c.outdent(); + c << "}" << endl << endl; + + // Header file suffix + h << endl; + h << HeaderBottom << endl; + h << "#endif /* " << guardName << " */" << endl; +} + +} // namespace wasm diff --git a/src/tools/wasm2c/wasm2c-builder.h b/src/tools/wasm2c/wasm2c-builder.h new file mode 100644 index 00000000000..8f90466630c --- /dev/null +++ b/src/tools/wasm2c/wasm2c-builder.h @@ -0,0 +1,46 @@ +/* + * Copyright 2026 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef wasm_tools_wasm2c_wasm2c_builder_h +#define wasm_tools_wasm2c_wasm2c_builder_h + +#include +#include + +#include "wasm.h" + +namespace wasm { + +class Wasm2CBuilder { +public: + struct Flags { + bool debug = false; + std::string moduleName = ""; + std::string headerName = ""; + }; + + Wasm2CBuilder(Flags f) : flags(f) {} + + void processWasm(Module* wasm, std::ostream& cOut, std::ostream& hOut); + +private: + Flags flags; + Module* module = nullptr; +}; + +} // namespace wasm + +#endif // wasm_tools_wasm2c_wasm2c_builder_h diff --git a/src/tools/wasm2c/wasm2c.cpp b/src/tools/wasm2c/wasm2c.cpp new file mode 100644 index 00000000000..ba5e5c70d7d --- /dev/null +++ b/src/tools/wasm2c/wasm2c.cpp @@ -0,0 +1,143 @@ +/* + * Copyright 2026 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// wasm2c console tool +// + +#include "parser/wat-parser.h" +#include "parsing.h" +#include "support/colors.h" +#include "support/command-line.h" +#include "support/file.h" +#include "support/path.h" +#include "tools/tool-options.h" +#include "tools/wasm2c/assertion-emitter.h" +#include "tools/wasm2c/wasm2c-builder.h" +#include "wasm-io.h" + +using namespace wasm; + +struct Wasm2COptions : public ToolOptions { + constexpr static const char* Wasm2COptionsCategory = "wasm2c options"; + + std::string output; + bool asserts = false; + std::string prefix; + std::string infile; + + Wasm2COptions() + : ToolOptions( + "wasm2c", + "Transform .wasm/.wat files to standard C source and headers") { + (*this) + .add("--output", + "-o", + "Output file path (derives .h by changing extension, writes both to " + "stdout if not specified)", + Wasm2COptionsCategory, + Arguments::One, + [this](Options*, const std::string& argument) { + output = argument; + Colors::setEnabled(false); + }) + .add("--allow-asserts", + "", + "Allow compilation of .wast testing asserts", + Wasm2COptionsCategory, + Arguments::Zero, + [this](Options*, const std::string&) { asserts = true; }) + .add("--prefix", + "-n", + "Set the name prefix for the generated C symbols", + Wasm2COptionsCategory, + Arguments::One, + [this](Options*, const std::string& argument) { prefix = argument; }) + .add_positional( + "INFILE", + Arguments::One, + [this](Options*, const std::string& argument) { infile = argument; }); + } +}; + +int main(int argc, const char* argv[]) { + Wasm2COptions options; + options.parse(argc, argv); + + Wasm2CBuilder::Flags flags; + flags.moduleName = options.prefix; + + if (options.asserts) { + // When `--allow-asserts` is passed (i.e. when running spec tests), we need + // to parse the extra s-expressions that come at the end of the `*.wast` + // file after the module is defined. Therefore, in this case, we can't use + // ModuleReader. + + auto input = read_file(options.infile, Flags::Text); + + auto script = WATParser::parseScript(input); + if (auto* err = script.getErr()) { + Fatal() << err->msg; + } + + Output cOut(options.output, Flags::Text); + AssertionEmitter emitter(*script, flags); + emitter.emit(cOut.getStream(), options.output); + } else { + Module wasm; + options.applyOptionsBeforeParse(wasm); + try { + ModuleReader reader; + reader.read(options.infile, wasm, ""); + } catch (ParseException& p) { + p.dump(std::cerr); + Fatal() << "error in parsing input"; + } + options.applyOptionsAfterParse(wasm); + + if (!options.output.empty()) { + // Derive output header file path (.h instead of .c) + std::string outputHPath = options.output; + size_t lastDot = outputHPath.find_last_of('.'); + if (lastDot != std::string::npos) { + outputHPath.replace(lastDot, std::string::npos, ".h"); + } else { + outputHPath += ".h"; + } + flags.headerName = Path::getBaseName(outputHPath); + + Output cOut(options.output, Flags::Text); + Output hOut(outputHPath, Flags::Text); + Wasm2CBuilder builder(flags); + builder.processWasm(&wasm, cOut.getStream(), hOut.getStream()); + } else { + // Write both to stdout sequentially + std::cout << "/* === HEADER FILE === */\n"; + std::stringstream hStream; + std::stringstream cStream; + + flags.headerName = "wasm.h"; // Default fallback + Wasm2CBuilder builder(flags); + builder.processWasm(&wasm, cStream, hStream); + + std::cout << hStream.str(); + std::cout << "\n/* === SOURCE FILE === */\n"; + std::cout << cStream.str(); + } + } + + flush_and_quick_exit(0); +} diff --git a/test/lit/wasm2c/minimal.wast b/test/lit/wasm2c/minimal.wast new file mode 100644 index 00000000000..9b17dff08e8 --- /dev/null +++ b/test/lit/wasm2c/minimal.wast @@ -0,0 +1,802 @@ +;; RUN: wasm2c %s -o %t.c +;; RUN: cat %t.h | filecheck %s --match-full-lines --strict-whitespace --check-prefixes CHECK-HEADER +;; RUN: cat %t.c | filecheck %s --match-full-lines --strict-whitespace -DHEADER_FILE=%basename_t.tmp.h --check-prefixes CHECK-SOURCE +(module) + +;; CHECK-HEADER: /* Automatically generated by wasm2c */ +;; CHECK-HEADER-NEXT: #ifndef WASM_H_GENERATED_TEST +;; CHECK-HEADER-NEXT: #define WASM_H_GENERATED_TEST +;; CHECK-HEADER-EMPTY: +;; CHECK-HEADER-NEXT: #include "wasm-rt.h" +;; CHECK-HEADER-EMPTY: +;; CHECK-HEADER-NEXT: #include +;; CHECK-HEADER-EMPTY: +;; CHECK-HEADER-NEXT: #ifndef WASM_RT_CORE_TYPES_DEFINED +;; CHECK-HEADER-NEXT: #define WASM_RT_CORE_TYPES_DEFINED +;; CHECK-HEADER-NEXT: typedef uint8_t u8; +;; CHECK-HEADER-NEXT: typedef int8_t s8; +;; CHECK-HEADER-NEXT: typedef uint16_t u16; +;; CHECK-HEADER-NEXT: typedef int16_t s16; +;; CHECK-HEADER-NEXT: typedef uint32_t u32; +;; CHECK-HEADER-NEXT: typedef int32_t s32; +;; CHECK-HEADER-NEXT: typedef uint64_t u64; +;; CHECK-HEADER-NEXT: typedef int64_t s64; +;; CHECK-HEADER-NEXT: typedef float f32; +;; CHECK-HEADER-NEXT: typedef double f64; +;; CHECK-HEADER-NEXT: #endif +;; CHECK-HEADER-EMPTY: +;; CHECK-HEADER-NEXT: #ifdef __cplusplus +;; CHECK-HEADER-NEXT: extern "C" { +;; CHECK-HEADER-NEXT: #endif +;; CHECK-HEADER-EMPTY: +;; CHECK-HEADER-NEXT: typedef struct w2c_test { +;; CHECK-HEADER-NEXT: char dummy_member; +;; CHECK-HEADER-NEXT: } w2c_test; +;; CHECK-HEADER-EMPTY: +;; CHECK-HEADER-NEXT: void wasm2c_test_instantiate(w2c_test*); +;; CHECK-HEADER-NEXT: void wasm2c_test_free(w2c_test*); +;; CHECK-HEADER-NEXT: wasm_rt_func_type_t wasm2c_test_get_func_type(uint32_t param_count, uint32_t result_count, ...); +;; CHECK-HEADER-EMPTY: +;; CHECK-HEADER-NEXT: #ifdef __cplusplus +;; CHECK-HEADER-NEXT: } +;; CHECK-HEADER-NEXT: #endif +;; CHECK-HEADER-EMPTY: +;; CHECK-HEADER-NEXT: #endif /* WASM_H_GENERATED_TEST */ + +;; CHECK-SOURCE: /* Automatically generated by wasm2c */ +;; CHECK-SOURCE-NEXT: #include +;; CHECK-SOURCE-NEXT: #include +;; CHECK-SOURCE-NEXT: #include +;; CHECK-SOURCE-NEXT: #include +;; CHECK-SOURCE-NEXT: #include +;; CHECK-SOURCE-NEXT: #if defined(__MINGW32__) +;; CHECK-SOURCE-NEXT: #include +;; CHECK-SOURCE-NEXT: #elif defined(_MSC_VER) +;; CHECK-SOURCE-NEXT: #include +;; CHECK-SOURCE-NEXT: #include +;; CHECK-SOURCE-NEXT: #define alloca _alloca +;; CHECK-SOURCE-NEXT: #elif defined(__FreeBSD__) || defined(__OpenBSD__) +;; CHECK-SOURCE-NEXT: #include +;; CHECK-SOURCE-NEXT: #else +;; CHECK-SOURCE-NEXT: #include +;; CHECK-SOURCE-NEXT: #endif +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #include "[[HEADER_FILE]]" +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: // Computes a pointer to an object of the given size in a little-endian memory. +;; CHECK-SOURCE-NEXT: // +;; CHECK-SOURCE-NEXT: // On a little-endian host, this is just &mem->data[addr] - the object's size is +;; CHECK-SOURCE-NEXT: // unused. On a big-endian host, it's &mem->data[mem->size - addr - n], where n +;; CHECK-SOURCE-NEXT: // is the object's size. +;; CHECK-SOURCE-NEXT: // +;; CHECK-SOURCE-NEXT: // Note that mem may be evaluated multiple times. +;; CHECK-SOURCE-NEXT: // +;; CHECK-SOURCE-NEXT: // Parameters: +;; CHECK-SOURCE-NEXT: // mem - The memory. +;; CHECK-SOURCE-NEXT: // addr - The address. +;; CHECK-SOURCE-NEXT: // n - The size of the object. +;; CHECK-SOURCE-NEXT: // +;; CHECK-SOURCE-NEXT: // Result: +;; CHECK-SOURCE-NEXT: // A pointer for an object of size n. +;; CHECK-SOURCE-NEXT: #if WASM_RT_BIG_ENDIAN +;; CHECK-SOURCE-NEXT: #define MEM_ADDR(mem, addr, n) ((mem)->data_end - (addr) - (n)) +;; CHECK-SOURCE-NEXT: #else +;; CHECK-SOURCE-NEXT: #define MEM_ADDR(mem, addr, n) &((mem)->data[addr]) +;; CHECK-SOURCE-NEXT: #endif +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: // We can only use Segue for this module if it uses a single unshared imported +;; CHECK-SOURCE-NEXT: // or exported memory +;; CHECK-SOURCE-NEXT: #if WASM_RT_USE_SEGUE && IS_SINGLE_UNSHARED_MEMORY +;; CHECK-SOURCE-NEXT: #define WASM_RT_USE_SEGUE_FOR_THIS_MODULE 1 +;; CHECK-SOURCE-NEXT: #else +;; CHECK-SOURCE-NEXT: #define WASM_RT_USE_SEGUE_FOR_THIS_MODULE 0 +;; CHECK-SOURCE-NEXT: #endif +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #if WASM_RT_USE_SEGUE_FOR_THIS_MODULE +;; CHECK-SOURCE-NEXT: // POSIX uses FS for TLS, GS is free +;; CHECK-SOURCE-NEXT: static inline void* wasm_rt_segue_read_base() { +;; CHECK-SOURCE-NEXT: if (wasm_rt_fsgsbase_inst_supported) { +;; CHECK-SOURCE-NEXT: return (void*)__builtin_ia32_rdgsbase64(); +;; CHECK-SOURCE-NEXT: } else { +;; CHECK-SOURCE-NEXT: return wasm_rt_syscall_get_segue_base(); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: static inline void wasm_rt_segue_write_base(void* base) { +;; CHECK-SOURCE-NEXT: #if WASM_RT_SEGUE_FREE_SEGMENT +;; CHECK-SOURCE-NEXT: if (wasm_rt_last_segment_val == base) { +;; CHECK-SOURCE-NEXT: return; +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: wasm_rt_last_segment_val = base; +;; CHECK-SOURCE-NEXT: #endif +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: if (wasm_rt_fsgsbase_inst_supported) { +;; CHECK-SOURCE-NEXT: __builtin_ia32_wrgsbase64((uintptr_t)base); +;; CHECK-SOURCE-NEXT: } else { +;; CHECK-SOURCE-NEXT: wasm_rt_syscall_set_segue_base(base); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: #define MEM_ADDR_MEMOP(mem, addr, n) ((uint8_t __seg_gs*)(uintptr_t)addr) +;; CHECK-SOURCE-NEXT: #else +;; CHECK-SOURCE-NEXT: #define MEM_ADDR_MEMOP(mem, addr, n) MEM_ADDR(mem, addr, n) +;; CHECK-SOURCE-NEXT: #endif +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define TRAP(x) (wasm_rt_trap(WASM_RT_TRAP_##x), 0) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #if WASM_RT_STACK_DEPTH_COUNT +;; CHECK-SOURCE-NEXT: #define FUNC_PROLOGUE \ +;; CHECK-SOURCE-NEXT: if (++wasm_rt_call_stack_depth > WASM_RT_MAX_CALL_STACK_DEPTH) \ +;; CHECK-SOURCE-NEXT: TRAP(EXHAUSTION); +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define FUNC_EPILOGUE --wasm_rt_call_stack_depth +;; CHECK-SOURCE-NEXT: #else +;; CHECK-SOURCE-NEXT: #define FUNC_PROLOGUE +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define FUNC_EPILOGUE +;; CHECK-SOURCE-NEXT: #endif +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define UNREACHABLE TRAP(UNREACHABLE) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static inline bool func_types_eq(const wasm_rt_func_type_t a, +;; CHECK-SOURCE-NEXT: const wasm_rt_func_type_t b) { +;; CHECK-SOURCE-NEXT: return (a == b) || LIKELY(a && b && !memcmp(a, b, 32)); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define CHECK_CALL_INDIRECT(table, ft, x) \ +;; CHECK-SOURCE-NEXT: (LIKELY((x) < table.size && table.data[x].func && \ +;; CHECK-SOURCE-NEXT: func_types_eq(ft, table.data[x].func_type)) || \ +;; CHECK-SOURCE-NEXT: TRAP(CALL_INDIRECT)) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define DO_CALL_INDIRECT(table, t, x, ...) ((t)table.data[x].func)(__VA_ARGS__) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define CALL_INDIRECT(table, t, ft, x, ...) \ +;; CHECK-SOURCE-NEXT: (CHECK_CALL_INDIRECT(table, ft, x), \ +;; CHECK-SOURCE-NEXT: DO_CALL_INDIRECT(table, t, x, __VA_ARGS__)) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static inline bool add_overflow(uint64_t a, uint64_t b, uint64_t* resptr) { +;; CHECK-SOURCE-NEXT: #if __has_builtin(__builtin_add_overflow) +;; CHECK-SOURCE-NEXT: return __builtin_add_overflow(a, b, resptr); +;; CHECK-SOURCE-NEXT: #elif defined(_MSC_VER) +;; CHECK-SOURCE-NEXT: return _addcarry_u64(0, a, b, resptr); +;; CHECK-SOURCE-NEXT: #else +;; CHECK-SOURCE-NEXT: #error "Missing implementation of __builtin_add_overflow or _addcarry_u64" +;; CHECK-SOURCE-NEXT: #endif +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define RANGE_CHECK(mem, offset, len) \ +;; CHECK-SOURCE-NEXT: do { \ +;; CHECK-SOURCE-NEXT: uint64_t res; \ +;; CHECK-SOURCE-NEXT: if (UNLIKELY(add_overflow(offset, len, &res))) \ +;; CHECK-SOURCE-NEXT: TRAP(OOB); \ +;; CHECK-SOURCE-NEXT: if (UNLIKELY(res > (mem)->size)) \ +;; CHECK-SOURCE-NEXT: TRAP(OOB); \ +;; CHECK-SOURCE-NEXT: } while (0); +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #if WASM_RT_USE_SEGUE_FOR_THIS_MODULE && WASM_RT_SANITY_CHECKS +;; CHECK-SOURCE-NEXT: #include +;; CHECK-SOURCE-NEXT: #define WASM_RT_CHECK_BASE(mem) \ +;; CHECK-SOURCE-NEXT: if (((uintptr_t)((mem)->data)) != ((uintptr_t)wasm_rt_segue_read_base())) { \ +;; CHECK-SOURCE-NEXT: puts("Segment register mismatch\n"); \ +;; CHECK-SOURCE-NEXT: abort(); \ +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: #else +;; CHECK-SOURCE-NEXT: #define WASM_RT_CHECK_BASE(mem) +;; CHECK-SOURCE-NEXT: #endif +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: // MEMCHECK_DEFAULT32 is an "accelerated" MEMCHECK used only for +;; CHECK-SOURCE-NEXT: // default-page-size, 32-bit memories. It may do nothing at all +;; CHECK-SOURCE-NEXT: // (if hardware bounds-checking is enabled via guard pages) +;; CHECK-SOURCE-NEXT: // or it may do a slightly faster RANGE_CHECK. +;; CHECK-SOURCE-NEXT: #if WASM_RT_MEMCHECK_GUARD_PAGES +;; CHECK-SOURCE-NEXT: #define MEMCHECK_DEFAULT32(mem, a, t) WASM_RT_CHECK_BASE(mem); +;; CHECK-SOURCE-NEXT: #else +;; CHECK-SOURCE-NEXT: #define MEMCHECK_DEFAULT32(mem, a, t) \ +;; CHECK-SOURCE-NEXT: WASM_RT_CHECK_BASE(mem); \ +;; CHECK-SOURCE-NEXT: if (UNLIKELY(a + (uint64_t)sizeof(t) > mem->size)) \ +;; CHECK-SOURCE-NEXT: TRAP(OOB); +;; CHECK-SOURCE-NEXT: #endif +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: // MEMCHECK_GENERAL can be used for any memory +;; CHECK-SOURCE-NEXT: #define MEMCHECK_GENERAL(mem, a, t) \ +;; CHECK-SOURCE-NEXT: WASM_RT_CHECK_BASE(mem); \ +;; CHECK-SOURCE-NEXT: RANGE_CHECK(mem, a, sizeof(t)); +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #ifdef __GNUC__ +;; CHECK-SOURCE-NEXT: #define FORCE_READ_INT(var) __asm__("" ::"r"(var)); +;; CHECK-SOURCE-NEXT: // Clang on Mips requires "f" constraints on floats +;; CHECK-SOURCE-NEXT: // See https://github.com/llvm/llvm-project/issues/64241 +;; CHECK-SOURCE-NEXT: #if defined(__clang__) && \ +;; CHECK-SOURCE-NEXT: (defined(mips) || defined(__mips__) || defined(__mips)) +;; CHECK-SOURCE-NEXT: #define FORCE_READ_FLOAT(var) __asm__("" ::"f"(var)); +;; CHECK-SOURCE-NEXT: #else +;; CHECK-SOURCE-NEXT: #define FORCE_READ_FLOAT(var) __asm__("" ::"r"(var)); +;; CHECK-SOURCE-NEXT: #endif +;; CHECK-SOURCE-NEXT: #else +;; CHECK-SOURCE-NEXT: #define FORCE_READ_INT(var) +;; CHECK-SOURCE-NEXT: #define FORCE_READ_FLOAT(var) +;; CHECK-SOURCE-NEXT: #endif +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static inline void load_data(u8* dest, const u8* src, size_t n) { +;; CHECK-SOURCE-NEXT: if (!n) { +;; CHECK-SOURCE-NEXT: return; +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: #if WASM_RT_BIG_ENDIAN +;; CHECK-SOURCE-NEXT: for (size_t i = 0; i < n; i++) { +;; CHECK-SOURCE-NEXT: dest[i] = src[n - i - 1]; +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: #else +;; CHECK-SOURCE-NEXT: wasm_rt_memcpy(dest, src, n); +;; CHECK-SOURCE-NEXT: #endif +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define LOAD_DATA(m, o, i, s) \ +;; CHECK-SOURCE-NEXT: do { \ +;; CHECK-SOURCE-NEXT: RANGE_CHECK((&m), o, s); \ +;; CHECK-SOURCE-NEXT: load_data(MEM_ADDR(&m, o, s), i, s); \ +;; CHECK-SOURCE-NEXT: } while (0) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define DEF_MEM_CHECKS0(name, shared, mem_type, ret_kw, return_type) \ +;; CHECK-SOURCE-NEXT: static inline return_type name##_default32(wasm_rt##shared##memory_t* mem, \ +;; CHECK-SOURCE-NEXT: u64 addr) { \ +;; CHECK-SOURCE-NEXT: MEMCHECK_DEFAULT32(mem, addr, mem_type); \ +;; CHECK-SOURCE-NEXT: ret_kw name##_unchecked(mem, addr); \ +;; CHECK-SOURCE-NEXT: } \ +;; CHECK-SOURCE-NEXT: static inline return_type name(wasm_rt##shared##memory_t* mem, u64 addr) { \ +;; CHECK-SOURCE-NEXT: MEMCHECK_GENERAL(mem, addr, mem_type); \ +;; CHECK-SOURCE-NEXT: ret_kw name##_unchecked(mem, addr); \ +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define DEF_MEM_CHECKS1(name, shared, mem_type, ret_kw, return_type, \ +;; CHECK-SOURCE-NEXT: val_type1) \ +;; CHECK-SOURCE-NEXT: static inline return_type name##_default32(wasm_rt##shared##memory_t* mem, \ +;; CHECK-SOURCE-NEXT: u64 addr, val_type1 val1) { \ +;; CHECK-SOURCE-NEXT: MEMCHECK_DEFAULT32(mem, addr, mem_type); \ +;; CHECK-SOURCE-NEXT: ret_kw name##_unchecked(mem, addr, val1); \ +;; CHECK-SOURCE-NEXT: } \ +;; CHECK-SOURCE-NEXT: static inline return_type name(wasm_rt##shared##memory_t* mem, u64 addr, \ +;; CHECK-SOURCE-NEXT: val_type1 val1) { \ +;; CHECK-SOURCE-NEXT: MEMCHECK_GENERAL(mem, addr, mem_type); \ +;; CHECK-SOURCE-NEXT: ret_kw name##_unchecked(mem, addr, val1); \ +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define DEF_MEM_CHECKS2(name, shared, mem_type, ret_kw, return_type, \ +;; CHECK-SOURCE-NEXT: val_type1, val_type2) \ +;; CHECK-SOURCE-NEXT: static inline return_type name##_default32(wasm_rt##shared##memory_t* mem, \ +;; CHECK-SOURCE-NEXT: u64 addr, val_type1 val1, \ +;; CHECK-SOURCE-NEXT: val_type2 val2) { \ +;; CHECK-SOURCE-NEXT: MEMCHECK_DEFAULT32(mem, addr, mem_type); \ +;; CHECK-SOURCE-NEXT: ret_kw name##_unchecked(mem, addr, val1, val2); \ +;; CHECK-SOURCE-NEXT: } \ +;; CHECK-SOURCE-NEXT: static inline return_type name(wasm_rt##shared##memory_t* mem, u64 addr, \ +;; CHECK-SOURCE-NEXT: val_type1 val1, val_type2 val2) { \ +;; CHECK-SOURCE-NEXT: MEMCHECK_GENERAL(mem, addr, mem_type); \ +;; CHECK-SOURCE-NEXT: ret_kw name##_unchecked(mem, addr, val1, val2); \ +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define DEFINE_LOAD(name, t1, t2, t3, force_read) \ +;; CHECK-SOURCE-NEXT: static inline t3 name##_unchecked(wasm_rt_memory_t* mem, u64 addr) { \ +;; CHECK-SOURCE-NEXT: t1 result; \ +;; CHECK-SOURCE-NEXT: wasm_rt_memcpy(&result, MEM_ADDR_MEMOP(mem, addr, sizeof(t1)), \ +;; CHECK-SOURCE-NEXT: sizeof(t1)); \ +;; CHECK-SOURCE-NEXT: force_read(result); \ +;; CHECK-SOURCE-NEXT: return (t3)(t2)result; \ +;; CHECK-SOURCE-NEXT: } \ +;; CHECK-SOURCE-NEXT: DEF_MEM_CHECKS0(name, _, t1, return, t3) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define DEFINE_STORE(name, t1, t2) \ +;; CHECK-SOURCE-NEXT: static inline void name##_unchecked(wasm_rt_memory_t* mem, u64 addr, \ +;; CHECK-SOURCE-NEXT: t2 value) { \ +;; CHECK-SOURCE-NEXT: t1 wrapped = (t1)value; \ +;; CHECK-SOURCE-NEXT: wasm_rt_memcpy(MEM_ADDR_MEMOP(mem, addr, sizeof(t1)), &wrapped, \ +;; CHECK-SOURCE-NEXT: sizeof(t1)); \ +;; CHECK-SOURCE-NEXT: } \ +;; CHECK-SOURCE-NEXT: DEF_MEM_CHECKS1(name, _, t1, , void, t2) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: DEFINE_LOAD(i32_load, u32, u32, u32, FORCE_READ_INT) +;; CHECK-SOURCE-NEXT: DEFINE_LOAD(i64_load, u64, u64, u64, FORCE_READ_INT) +;; CHECK-SOURCE-NEXT: DEFINE_LOAD(f32_load, f32, f32, f32, FORCE_READ_FLOAT) +;; CHECK-SOURCE-NEXT: DEFINE_LOAD(f64_load, f64, f64, f64, FORCE_READ_FLOAT) +;; CHECK-SOURCE-NEXT: DEFINE_LOAD(i32_load8_s, s8, s32, u32, FORCE_READ_INT) +;; CHECK-SOURCE-NEXT: DEFINE_LOAD(i64_load8_s, s8, s64, u64, FORCE_READ_INT) +;; CHECK-SOURCE-NEXT: DEFINE_LOAD(i32_load8_u, u8, u32, u32, FORCE_READ_INT) +;; CHECK-SOURCE-NEXT: DEFINE_LOAD(i64_load8_u, u8, u64, u64, FORCE_READ_INT) +;; CHECK-SOURCE-NEXT: DEFINE_LOAD(i32_load16_s, s16, s32, u32, FORCE_READ_INT) +;; CHECK-SOURCE-NEXT: DEFINE_LOAD(i64_load16_s, s16, s64, u64, FORCE_READ_INT) +;; CHECK-SOURCE-NEXT: DEFINE_LOAD(i32_load16_u, u16, u32, u32, FORCE_READ_INT) +;; CHECK-SOURCE-NEXT: DEFINE_LOAD(i64_load16_u, u16, u64, u64, FORCE_READ_INT) +;; CHECK-SOURCE-NEXT: DEFINE_LOAD(i64_load32_s, s32, s64, u64, FORCE_READ_INT) +;; CHECK-SOURCE-NEXT: DEFINE_LOAD(i64_load32_u, u32, u64, u64, FORCE_READ_INT) +;; CHECK-SOURCE-NEXT: DEFINE_STORE(i32_store, u32, u32) +;; CHECK-SOURCE-NEXT: DEFINE_STORE(i64_store, u64, u64) +;; CHECK-SOURCE-NEXT: DEFINE_STORE(f32_store, f32, f32) +;; CHECK-SOURCE-NEXT: DEFINE_STORE(f64_store, f64, f64) +;; CHECK-SOURCE-NEXT: DEFINE_STORE(i32_store8, u8, u32) +;; CHECK-SOURCE-NEXT: DEFINE_STORE(i32_store16, u16, u32) +;; CHECK-SOURCE-NEXT: DEFINE_STORE(i64_store8, u8, u64) +;; CHECK-SOURCE-NEXT: DEFINE_STORE(i64_store16, u16, u64) +;; CHECK-SOURCE-NEXT: DEFINE_STORE(i64_store32, u32, u64) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #if defined(_MSC_VER) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: // Adapted from +;; CHECK-SOURCE-NEXT: // https://github.com/nemequ/portable-snippets/blob/master/builtin/builtin.h +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static inline int I64_CLZ(unsigned long long v) { +;; CHECK-SOURCE-NEXT: unsigned long r = 0; +;; CHECK-SOURCE-NEXT: #if defined(_M_AMD64) || defined(_M_ARM) +;; CHECK-SOURCE-NEXT: if (_BitScanReverse64(&r, v)) { +;; CHECK-SOURCE-NEXT: return 63 - r; +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: #else +;; CHECK-SOURCE-NEXT: if (_BitScanReverse(&r, (unsigned long)(v >> 32))) { +;; CHECK-SOURCE-NEXT: return 31 - r; +;; CHECK-SOURCE-NEXT: } else if (_BitScanReverse(&r, (unsigned long)v)) { +;; CHECK-SOURCE-NEXT: return 63 - r; +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: #endif +;; CHECK-SOURCE-NEXT: return 64; +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static inline int I32_CLZ(unsigned long v) { +;; CHECK-SOURCE-NEXT: unsigned long r = 0; +;; CHECK-SOURCE-NEXT: if (_BitScanReverse(&r, v)) { +;; CHECK-SOURCE-NEXT: return 31 - r; +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: return 32; +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static inline int I64_CTZ(unsigned long long v) { +;; CHECK-SOURCE-NEXT: if (!v) { +;; CHECK-SOURCE-NEXT: return 64; +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: unsigned long r = 0; +;; CHECK-SOURCE-NEXT: #if defined(_M_AMD64) || defined(_M_ARM) +;; CHECK-SOURCE-NEXT: _BitScanForward64(&r, v); +;; CHECK-SOURCE-NEXT: return (int)r; +;; CHECK-SOURCE-NEXT: #else +;; CHECK-SOURCE-NEXT: if (_BitScanForward(&r, (unsigned int)(v))) { +;; CHECK-SOURCE-NEXT: return (int)(r); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: _BitScanForward(&r, (unsigned int)(v >> 32)); +;; CHECK-SOURCE-NEXT: return (int)(r + 32); +;; CHECK-SOURCE-NEXT: #endif +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static inline int I32_CTZ(unsigned long v) { +;; CHECK-SOURCE-NEXT: if (!v) { +;; CHECK-SOURCE-NEXT: return 32; +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: unsigned long r = 0; +;; CHECK-SOURCE-NEXT: _BitScanForward(&r, v); +;; CHECK-SOURCE-NEXT: return (int)r; +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define POPCOUNT_DEFINE_PORTABLE(f_n, T) \ +;; CHECK-SOURCE-NEXT: static inline u32 f_n(T x) { \ +;; CHECK-SOURCE-NEXT: x = x - ((x >> 1) & (T) ~(T)0 / 3); \ +;; CHECK-SOURCE-NEXT: x = (x & (T) ~(T)0 / 15 * 3) + ((x >> 2) & (T) ~(T)0 / 15 * 3); \ +;; CHECK-SOURCE-NEXT: x = (x + (x >> 4)) & (T) ~(T)0 / 255 * 15; \ +;; CHECK-SOURCE-NEXT: return (T)(x * ((T) ~(T)0 / 255)) >> (sizeof(T) - 1) * 8; \ +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: POPCOUNT_DEFINE_PORTABLE(I32_POPCNT, u32) +;; CHECK-SOURCE-NEXT: POPCOUNT_DEFINE_PORTABLE(I64_POPCNT, u64) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #undef POPCOUNT_DEFINE_PORTABLE +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #else +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define I32_CLZ(x) ((x) ? __builtin_clz(x) : 32) +;; CHECK-SOURCE-NEXT: #define I64_CLZ(x) ((x) ? __builtin_clzll(x) : 64) +;; CHECK-SOURCE-NEXT: #define I32_CTZ(x) ((x) ? __builtin_ctz(x) : 32) +;; CHECK-SOURCE-NEXT: #define I64_CTZ(x) ((x) ? __builtin_ctzll(x) : 64) +;; CHECK-SOURCE-NEXT: #define I32_POPCNT(x) (__builtin_popcount(x)) +;; CHECK-SOURCE-NEXT: #define I64_POPCNT(x) (__builtin_popcountll(x)) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #endif +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define DIV_S(ut, min, x, y) \ +;; CHECK-SOURCE-NEXT: ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \ +;; CHECK-SOURCE-NEXT: : (UNLIKELY((x) == min && (y) == -1)) ? TRAP(INT_OVERFLOW) \ +;; CHECK-SOURCE-NEXT: : (ut)((x) / (y))) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define REM_S(ut, min, x, y) \ +;; CHECK-SOURCE-NEXT: ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \ +;; CHECK-SOURCE-NEXT: : (UNLIKELY((x) == min && (y) == -1)) ? 0 \ +;; CHECK-SOURCE-NEXT: : (ut)((x) % (y))) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define I32_DIV_S(x, y) DIV_S(u32, INT32_MIN, (s32)x, (s32)y) +;; CHECK-SOURCE-NEXT: #define I64_DIV_S(x, y) DIV_S(u64, INT64_MIN, (s64)x, (s64)y) +;; CHECK-SOURCE-NEXT: #define I32_REM_S(x, y) REM_S(u32, INT32_MIN, (s32)x, (s32)y) +;; CHECK-SOURCE-NEXT: #define I64_REM_S(x, y) REM_S(u64, INT64_MIN, (s64)x, (s64)y) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define DIVREM_U(op, x, y) \ +;; CHECK-SOURCE-NEXT: ((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) : ((x)op(y))) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define DIV_U(x, y) DIVREM_U(/, x, y) +;; CHECK-SOURCE-NEXT: #define REM_U(x, y) DIVREM_U(%, x, y) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define ROTL(x, y, mask) \ +;; CHECK-SOURCE-NEXT: (((x) << ((y) & (mask))) | ((x) >> (((mask) - (y) + 1) & (mask)))) +;; CHECK-SOURCE-NEXT: #define ROTR(x, y, mask) \ +;; CHECK-SOURCE-NEXT: (((x) >> ((y) & (mask))) | ((x) << (((mask) - (y) + 1) & (mask)))) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define I32_ROTL(x, y) ROTL(x, y, 31) +;; CHECK-SOURCE-NEXT: #define I64_ROTL(x, y) ROTL(x, y, 63) +;; CHECK-SOURCE-NEXT: #define I32_ROTR(x, y) ROTR(x, y, 31) +;; CHECK-SOURCE-NEXT: #define I64_ROTR(x, y) ROTR(x, y, 63) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define FMIN(x, y) \ +;; CHECK-SOURCE-NEXT: ((UNLIKELY((x) != (x))) ? NAN \ +;; CHECK-SOURCE-NEXT: : (UNLIKELY((y) != (y))) ? NAN \ +;; CHECK-SOURCE-NEXT: : (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? x : y) \ +;; CHECK-SOURCE-NEXT: : (x < y) ? x \ +;; CHECK-SOURCE-NEXT: : y) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define FMAX(x, y) \ +;; CHECK-SOURCE-NEXT: ((UNLIKELY((x) != (x))) ? NAN \ +;; CHECK-SOURCE-NEXT: : (UNLIKELY((y) != (y))) ? NAN \ +;; CHECK-SOURCE-NEXT: : (UNLIKELY((x) == 0 && (y) == 0)) ? (signbit(x) ? y : x) \ +;; CHECK-SOURCE-NEXT: : (x > y) ? x \ +;; CHECK-SOURCE-NEXT: : y) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define TRUNC_S(ut, st, ft, min, minop, max, x) \ +;; CHECK-SOURCE-NEXT: ((UNLIKELY((x) != (x))) ? TRAP(INVALID_CONVERSION) \ +;; CHECK-SOURCE-NEXT: : (UNLIKELY(!((x)minop(min) && (x) < (max)))) ? TRAP(INT_OVERFLOW) \ +;; CHECK-SOURCE-NEXT: : (ut)(st)(x)) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define I32_TRUNC_S_F32(x) \ +;; CHECK-SOURCE-NEXT: TRUNC_S(u32, s32, f32, (f32)INT32_MIN, >=, 2147483648.f, x) +;; CHECK-SOURCE-NEXT: #define I64_TRUNC_S_F32(x) \ +;; CHECK-SOURCE-NEXT: TRUNC_S(u64, s64, f32, (f32)INT64_MIN, >=, (f32)INT64_MAX, x) +;; CHECK-SOURCE-NEXT: #define I32_TRUNC_S_F64(x) \ +;; CHECK-SOURCE-NEXT: TRUNC_S(u32, s32, f64, -2147483649., >, 2147483648., x) +;; CHECK-SOURCE-NEXT: #define I64_TRUNC_S_F64(x) \ +;; CHECK-SOURCE-NEXT: TRUNC_S(u64, s64, f64, (f64)INT64_MIN, >=, (f64)INT64_MAX, x) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define TRUNC_U(ut, ft, max, x) \ +;; CHECK-SOURCE-NEXT: ((UNLIKELY((x) != (x))) ? TRAP(INVALID_CONVERSION) \ +;; CHECK-SOURCE-NEXT: : (UNLIKELY(!((x) > (ft) - 1 && (x) < (max)))) ? TRAP(INT_OVERFLOW) \ +;; CHECK-SOURCE-NEXT: : (ut)(x)) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define I32_TRUNC_U_F32(x) TRUNC_U(u32, f32, 4294967296.f, x) +;; CHECK-SOURCE-NEXT: #define I64_TRUNC_U_F32(x) TRUNC_U(u64, f32, (f32)UINT64_MAX, x) +;; CHECK-SOURCE-NEXT: #define I32_TRUNC_U_F64(x) TRUNC_U(u32, f64, 4294967296., x) +;; CHECK-SOURCE-NEXT: #define I64_TRUNC_U_F64(x) TRUNC_U(u64, f64, (f64)UINT64_MAX, x) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define TRUNC_SAT_S(ut, st, ft, min, smin, minop, max, smax, x) \ +;; CHECK-SOURCE-NEXT: ((UNLIKELY((x) != (x))) ? 0 \ +;; CHECK-SOURCE-NEXT: : (UNLIKELY(!((x)minop(min)))) ? smin \ +;; CHECK-SOURCE-NEXT: : (UNLIKELY(!((x) < (max)))) ? smax \ +;; CHECK-SOURCE-NEXT: : (ut)(st)(x)) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define I32_TRUNC_SAT_S_F32(x) \ +;; CHECK-SOURCE-NEXT: TRUNC_SAT_S(u32, s32, f32, (f32)INT32_MIN, INT32_MIN, >=, 2147483648.f, \ +;; CHECK-SOURCE-NEXT: INT32_MAX, x) +;; CHECK-SOURCE-NEXT: #define I64_TRUNC_SAT_S_F32(x) \ +;; CHECK-SOURCE-NEXT: TRUNC_SAT_S(u64, s64, f32, (f32)INT64_MIN, INT64_MIN, >=, (f32)INT64_MAX, \ +;; CHECK-SOURCE-NEXT: INT64_MAX, x) +;; CHECK-SOURCE-NEXT: #define I32_TRUNC_SAT_S_F64(x) \ +;; CHECK-SOURCE-NEXT: TRUNC_SAT_S(u32, s32, f64, -2147483649., INT32_MIN, >, 2147483648., \ +;; CHECK-SOURCE-NEXT: INT32_MAX, x) +;; CHECK-SOURCE-NEXT: #define I64_TRUNC_SAT_S_F64(x) \ +;; CHECK-SOURCE-NEXT: TRUNC_SAT_S(u64, s64, f64, (f64)INT64_MIN, INT64_MIN, >=, (f64)INT64_MAX, \ +;; CHECK-SOURCE-NEXT: INT64_MAX, x) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define TRUNC_SAT_U(ut, ft, max, smax, x) \ +;; CHECK-SOURCE-NEXT: ((UNLIKELY((x) != (x))) ? 0 \ +;; CHECK-SOURCE-NEXT: : (UNLIKELY(!((x) > (ft) - 1))) ? 0 \ +;; CHECK-SOURCE-NEXT: : (UNLIKELY(!((x) < (max)))) ? smax \ +;; CHECK-SOURCE-NEXT: : (ut)(x)) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define I32_TRUNC_SAT_U_F32(x) \ +;; CHECK-SOURCE-NEXT: TRUNC_SAT_U(u32, f32, 4294967296.f, UINT32_MAX, x) +;; CHECK-SOURCE-NEXT: #define I64_TRUNC_SAT_U_F32(x) \ +;; CHECK-SOURCE-NEXT: TRUNC_SAT_U(u64, f32, (f32)UINT64_MAX, UINT64_MAX, x) +;; CHECK-SOURCE-NEXT: #define I32_TRUNC_SAT_U_F64(x) TRUNC_SAT_U(u32, f64, 4294967296., UINT32_MAX, x) +;; CHECK-SOURCE-NEXT: #define I64_TRUNC_SAT_U_F64(x) \ +;; CHECK-SOURCE-NEXT: TRUNC_SAT_U(u64, f64, (f64)UINT64_MAX, UINT64_MAX, x) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define DEFINE_REINTERPRET(name, t1, t2) \ +;; CHECK-SOURCE-NEXT: static inline t2 name(t1 x) { \ +;; CHECK-SOURCE-NEXT: t2 result; \ +;; CHECK-SOURCE-NEXT: wasm_rt_memcpy(&result, &x, sizeof(result)); \ +;; CHECK-SOURCE-NEXT: return result; \ +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: DEFINE_REINTERPRET(f32_reinterpret_i32, u32, f32) +;; CHECK-SOURCE-NEXT: DEFINE_REINTERPRET(i32_reinterpret_f32, f32, u32) +;; CHECK-SOURCE-NEXT: DEFINE_REINTERPRET(f64_reinterpret_i64, u64, f64) +;; CHECK-SOURCE-NEXT: DEFINE_REINTERPRET(i64_reinterpret_f64, f64, u64) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static float quiet_nanf(float x) { +;; CHECK-SOURCE-NEXT: uint32_t tmp; +;; CHECK-SOURCE-NEXT: wasm_rt_memcpy(&tmp, &x, 4); +;; CHECK-SOURCE-NEXT: tmp |= 0x7fc00000lu; +;; CHECK-SOURCE-NEXT: wasm_rt_memcpy(&x, &tmp, 4); +;; CHECK-SOURCE-NEXT: return x; +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static double quiet_nan(double x) { +;; CHECK-SOURCE-NEXT: uint64_t tmp; +;; CHECK-SOURCE-NEXT: wasm_rt_memcpy(&tmp, &x, 8); +;; CHECK-SOURCE-NEXT: tmp |= 0x7ff8000000000000llu; +;; CHECK-SOURCE-NEXT: wasm_rt_memcpy(&x, &tmp, 8); +;; CHECK-SOURCE-NEXT: return x; +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static double wasm_quiet(double x) { +;; CHECK-SOURCE-NEXT: if (UNLIKELY(isnan(x))) { +;; CHECK-SOURCE-NEXT: return quiet_nan(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: return x; +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static float wasm_quietf(float x) { +;; CHECK-SOURCE-NEXT: if (UNLIKELY(isnan(x))) { +;; CHECK-SOURCE-NEXT: return quiet_nanf(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: return x; +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static double wasm_floor(double x) { +;; CHECK-SOURCE-NEXT: if (UNLIKELY(isnan(x))) { +;; CHECK-SOURCE-NEXT: return quiet_nan(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: return floor(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static float wasm_floorf(float x) { +;; CHECK-SOURCE-NEXT: if (UNLIKELY(isnan(x))) { +;; CHECK-SOURCE-NEXT: return quiet_nanf(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: return floorf(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static double wasm_ceil(double x) { +;; CHECK-SOURCE-NEXT: if (UNLIKELY(isnan(x))) { +;; CHECK-SOURCE-NEXT: return quiet_nan(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: return ceil(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static float wasm_ceilf(float x) { +;; CHECK-SOURCE-NEXT: if (UNLIKELY(isnan(x))) { +;; CHECK-SOURCE-NEXT: return quiet_nanf(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: return ceilf(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static double wasm_trunc(double x) { +;; CHECK-SOURCE-NEXT: if (UNLIKELY(isnan(x))) { +;; CHECK-SOURCE-NEXT: return quiet_nan(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: return trunc(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static float wasm_truncf(float x) { +;; CHECK-SOURCE-NEXT: if (UNLIKELY(isnan(x))) { +;; CHECK-SOURCE-NEXT: return quiet_nanf(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: return truncf(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static float wasm_nearbyintf(float x) { +;; CHECK-SOURCE-NEXT: if (UNLIKELY(isnan(x))) { +;; CHECK-SOURCE-NEXT: return quiet_nanf(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: return nearbyintf(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static double wasm_nearbyint(double x) { +;; CHECK-SOURCE-NEXT: if (UNLIKELY(isnan(x))) { +;; CHECK-SOURCE-NEXT: return quiet_nan(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: return nearbyint(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static float wasm_fabsf(float x) { +;; CHECK-SOURCE-NEXT: if (UNLIKELY(isnan(x))) { +;; CHECK-SOURCE-NEXT: uint32_t tmp; +;; CHECK-SOURCE-NEXT: wasm_rt_memcpy(&tmp, &x, 4); +;; CHECK-SOURCE-NEXT: tmp = tmp & ~(1UL << 31); +;; CHECK-SOURCE-NEXT: wasm_rt_memcpy(&x, &tmp, 4); +;; CHECK-SOURCE-NEXT: return x; +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: return fabsf(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static double wasm_fabs(double x) { +;; CHECK-SOURCE-NEXT: if (UNLIKELY(isnan(x))) { +;; CHECK-SOURCE-NEXT: uint64_t tmp; +;; CHECK-SOURCE-NEXT: wasm_rt_memcpy(&tmp, &x, 8); +;; CHECK-SOURCE-NEXT: tmp = tmp & ~(1ULL << 63); +;; CHECK-SOURCE-NEXT: wasm_rt_memcpy(&x, &tmp, 8); +;; CHECK-SOURCE-NEXT: return x; +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: return fabs(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static double wasm_sqrt(double x) { +;; CHECK-SOURCE-NEXT: if (UNLIKELY(isnan(x))) { +;; CHECK-SOURCE-NEXT: return quiet_nan(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: return sqrt(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static float wasm_sqrtf(float x) { +;; CHECK-SOURCE-NEXT: if (UNLIKELY(isnan(x))) { +;; CHECK-SOURCE-NEXT: return quiet_nanf(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: return sqrtf(x); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static inline void memory_fill(wasm_rt_memory_t* mem, u64 d, u32 val, u64 n) { +;; CHECK-SOURCE-NEXT: RANGE_CHECK(mem, d, n); +;; CHECK-SOURCE-NEXT: memset(MEM_ADDR(mem, d, n), val, n); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static inline void memory_copy(wasm_rt_memory_t* dest, +;; CHECK-SOURCE-NEXT: const wasm_rt_memory_t* src, +;; CHECK-SOURCE-NEXT: u64 dest_addr, +;; CHECK-SOURCE-NEXT: u64 src_addr, +;; CHECK-SOURCE-NEXT: u64 n) { +;; CHECK-SOURCE-NEXT: RANGE_CHECK(dest, dest_addr, n); +;; CHECK-SOURCE-NEXT: RANGE_CHECK(src, src_addr, n); +;; CHECK-SOURCE-NEXT: memmove(MEM_ADDR(dest, dest_addr, n), MEM_ADDR(src, src_addr, n), n); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static inline void memory_init(wasm_rt_memory_t* dest, +;; CHECK-SOURCE-NEXT: const u8* src, +;; CHECK-SOURCE-NEXT: u32 src_size, +;; CHECK-SOURCE-NEXT: u64 dest_addr, +;; CHECK-SOURCE-NEXT: u32 src_addr, +;; CHECK-SOURCE-NEXT: u32 n) { +;; CHECK-SOURCE-NEXT: if (UNLIKELY(src_addr + (uint64_t)n > src_size)) +;; CHECK-SOURCE-NEXT: TRAP(OOB); +;; CHECK-SOURCE-NEXT: LOAD_DATA((*dest), dest_addr, src + src_addr, n); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: typedef enum { RefFunc, RefNull, GlobalGet } wasm_elem_segment_expr_type_t; +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: typedef struct { +;; CHECK-SOURCE-NEXT: wasm_elem_segment_expr_type_t expr_type; +;; CHECK-SOURCE-NEXT: wasm_rt_func_type_t type; +;; CHECK-SOURCE-NEXT: wasm_rt_function_ptr_t func; +;; CHECK-SOURCE-NEXT: wasm_rt_tailcallee_t func_tailcallee; +;; CHECK-SOURCE-NEXT: size_t module_offset; +;; CHECK-SOURCE-NEXT: } wasm_elem_segment_expr_t; +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: static inline void funcref_table_init(wasm_rt_funcref_table_t* dest, +;; CHECK-SOURCE-NEXT: const wasm_elem_segment_expr_t* src, +;; CHECK-SOURCE-NEXT: u32 src_size, +;; CHECK-SOURCE-NEXT: u64 dest_addr, +;; CHECK-SOURCE-NEXT: u32 src_addr, +;; CHECK-SOURCE-NEXT: u32 n, +;; CHECK-SOURCE-NEXT: void* module_instance) { +;; CHECK-SOURCE-NEXT: if (UNLIKELY(src_addr + (uint64_t)n > src_size)) +;; CHECK-SOURCE-NEXT: TRAP(OOB); +;; CHECK-SOURCE-NEXT: RANGE_CHECK(dest, dest_addr, n); +;; CHECK-SOURCE-NEXT: for (u32 i = 0; i < n; i++) { +;; CHECK-SOURCE-NEXT: const wasm_elem_segment_expr_t* const src_expr = &src[src_addr + i]; +;; CHECK-SOURCE-NEXT: wasm_rt_funcref_t* const dest_val = &(dest->data[dest_addr + i]); +;; CHECK-SOURCE-NEXT: switch (src_expr->expr_type) { +;; CHECK-SOURCE-NEXT: case RefFunc: +;; CHECK-SOURCE-NEXT: *dest_val = (wasm_rt_funcref_t){ +;; CHECK-SOURCE-NEXT: src_expr->type, src_expr->func, src_expr->func_tailcallee, +;; CHECK-SOURCE-NEXT: (char*)module_instance + src_expr->module_offset}; +;; CHECK-SOURCE-NEXT: break; +;; CHECK-SOURCE-NEXT: case RefNull: +;; CHECK-SOURCE-NEXT: *dest_val = wasm_rt_funcref_null_value; +;; CHECK-SOURCE-NEXT: break; +;; CHECK-SOURCE-NEXT: case GlobalGet: +;; CHECK-SOURCE-NEXT: *dest_val = **(wasm_rt_funcref_t**)((char*)module_instance + +;; CHECK-SOURCE-NEXT: src_expr->module_offset); +;; CHECK-SOURCE-NEXT: break; +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: // Currently wasm2c only supports initializing externref tables with ref.null. +;; CHECK-SOURCE-NEXT: static inline void externref_table_init(wasm_rt_externref_table_t* dest, +;; CHECK-SOURCE-NEXT: u32 src_size, +;; CHECK-SOURCE-NEXT: u64 dest_addr, +;; CHECK-SOURCE-NEXT: u32 src_addr, +;; CHECK-SOURCE-NEXT: u32 n) { +;; CHECK-SOURCE-NEXT: if (UNLIKELY(src_addr + (uint64_t)n > src_size)) +;; CHECK-SOURCE-NEXT: TRAP(OOB); +;; CHECK-SOURCE-NEXT: RANGE_CHECK(dest, dest_addr, n); +;; CHECK-SOURCE-NEXT: for (u32 i = 0; i < n; i++) { +;; CHECK-SOURCE-NEXT: dest->data[dest_addr + i] = wasm_rt_externref_null_value; +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define DEFINE_TABLE_COPY(type) \ +;; CHECK-SOURCE-NEXT: static inline void type##_table_copy(wasm_rt_##type##_table_t* dest, \ +;; CHECK-SOURCE-NEXT: const wasm_rt_##type##_table_t* src, \ +;; CHECK-SOURCE-NEXT: u64 dest_addr, u64 src_addr, u64 n) { \ +;; CHECK-SOURCE-NEXT: RANGE_CHECK(dest, dest_addr, n); \ +;; CHECK-SOURCE-NEXT: RANGE_CHECK(src, src_addr, n); \ +;; CHECK-SOURCE-NEXT: memmove(dest->data + dest_addr, src->data + src_addr, \ +;; CHECK-SOURCE-NEXT: n * sizeof(wasm_rt_##type##_t)); \ +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: DEFINE_TABLE_COPY(funcref) +;; CHECK-SOURCE-NEXT: DEFINE_TABLE_COPY(externref) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define DEFINE_TABLE_GET(type) \ +;; CHECK-SOURCE-NEXT: static inline wasm_rt_##type##_t type##_table_get( \ +;; CHECK-SOURCE-NEXT: const wasm_rt_##type##_table_t* table, u64 i) { \ +;; CHECK-SOURCE-NEXT: if (UNLIKELY(i >= table->size)) \ +;; CHECK-SOURCE-NEXT: TRAP(OOB); \ +;; CHECK-SOURCE-NEXT: return table->data[i]; \ +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: DEFINE_TABLE_GET(funcref) +;; CHECK-SOURCE-NEXT: DEFINE_TABLE_GET(externref) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define DEFINE_TABLE_SET(type) \ +;; CHECK-SOURCE-NEXT: static inline void type##_table_set(const wasm_rt_##type##_table_t* table, \ +;; CHECK-SOURCE-NEXT: u64 i, const wasm_rt_##type##_t val) { \ +;; CHECK-SOURCE-NEXT: if (UNLIKELY(i >= table->size)) \ +;; CHECK-SOURCE-NEXT: TRAP(OOB); \ +;; CHECK-SOURCE-NEXT: table->data[i] = val; \ +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: DEFINE_TABLE_SET(funcref) +;; CHECK-SOURCE-NEXT: DEFINE_TABLE_SET(externref) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #define DEFINE_TABLE_FILL(type) \ +;; CHECK-SOURCE-NEXT: static inline void type##_table_fill(const wasm_rt_##type##_table_t* table, \ +;; CHECK-SOURCE-NEXT: u64 d, const wasm_rt_##type##_t val, \ +;; CHECK-SOURCE-NEXT: u64 n) { \ +;; CHECK-SOURCE-NEXT: RANGE_CHECK(table, d, n); \ +;; CHECK-SOURCE-NEXT: for (uint32_t i = d; i < d + n; i++) { \ +;; CHECK-SOURCE-NEXT: table->data[i] = val; \ +;; CHECK-SOURCE-NEXT: } \ +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: DEFINE_TABLE_FILL(funcref) +;; CHECK-SOURCE-NEXT: DEFINE_TABLE_FILL(externref) +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #if defined(__GNUC__) || defined(__clang__) +;; CHECK-SOURCE-NEXT: #define FUNC_TYPE_DECL_EXTERN_T(x) extern const char* const x +;; CHECK-SOURCE-NEXT: #define FUNC_TYPE_EXTERN_T(x) const char* const x +;; CHECK-SOURCE-NEXT: #define FUNC_TYPE_T(x) static const char* const x +;; CHECK-SOURCE-NEXT: #else +;; CHECK-SOURCE-NEXT: #define FUNC_TYPE_DECL_EXTERN_T(x) extern const char x[] +;; CHECK-SOURCE-NEXT: #define FUNC_TYPE_EXTERN_T(x) const char x[] +;; CHECK-SOURCE-NEXT: #define FUNC_TYPE_T(x) static const char x[] +;; CHECK-SOURCE-NEXT: #endif +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #if (__STDC_VERSION__ < 201112L) && !defined(static_assert) +;; CHECK-SOURCE-NEXT: #define static_assert(X) \ +;; CHECK-SOURCE-NEXT: extern int(*assertion(void))[!!sizeof(struct { int x : (X) ? 2 : -1; })]; +;; CHECK-SOURCE-NEXT: #endif +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: #ifdef _MSC_VER +;; CHECK-SOURCE-NEXT: #define WEAK_FUNC_DECL(func, fallback) \ +;; CHECK-SOURCE-NEXT: __pragma(comment(linker, "/alternatename:" #func "=" #fallback)) \ +;; CHECK-SOURCE-NEXT: \ +;; CHECK-SOURCE-NEXT: void \ +;; CHECK-SOURCE-NEXT: fallback(void** instance_ptr, void* tail_call_stack, \ +;; CHECK-SOURCE-NEXT: wasm_rt_tailcallee_t* next) +;; CHECK-SOURCE-NEXT: #else +;; CHECK-SOURCE-NEXT: #define WEAK_FUNC_DECL(func, fallback) \ +;; CHECK-SOURCE-NEXT: __attribute__((weak)) void func(void** instance_ptr, void* tail_call_stack, \ +;; CHECK-SOURCE-NEXT: wasm_rt_tailcallee_t* next) +;; CHECK-SOURCE-NEXT: #endif +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: void wasm2c_test_instantiate(w2c_test* instance) { +;; CHECK-SOURCE-NEXT: assert(wasm_rt_is_initialized()); +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: void wasm2c_test_free(w2c_test* instance) { +;; CHECK-SOURCE-NEXT: } +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: wasm_rt_func_type_t wasm2c_test_get_func_type(uint32_t param_count, uint32_t result_count, ...) { +;; CHECK-SOURCE-NEXT: va_list args; +;; CHECK-SOURCE-EMPTY: +;; CHECK-SOURCE-NEXT: return NULL; +;; CHECK-SOURCE-NEXT: } diff --git a/test/spec/minimal.wast b/test/spec/minimal.wast new file mode 100644 index 00000000000..3af8f254547 --- /dev/null +++ b/test/spec/minimal.wast @@ -0,0 +1 @@ +(module) diff --git a/third_party/picosha2 b/third_party/picosha2 new file mode 160000 index 00000000000..161cb3fc417 --- /dev/null +++ b/third_party/picosha2 @@ -0,0 +1 @@ +Subproject commit 161cb3fc4170fa7a3eca9e582cebd27cc4d1fe29 diff --git a/third_party/simde b/third_party/simde new file mode 160000 index 00000000000..f3e8262173b --- /dev/null +++ b/third_party/simde @@ -0,0 +1 @@ +Subproject commit f3e8262173b7089db9a9d57a9ecef8dd07ad9c97