Translates WebAssembly modules to portable C. Inspired by wabt's wasm2c.
Working towards WebAssembly as the Elusive Universal Binary:
↗ different
source code → WebAssembly → C89 → OSes,
(C++, Rust, ...) ↘ CPUs
- Supports WebAssembly 2.0 and these extensions:
- Extended constant expressions (finalized in WebAssembly 3.0)
- Threads and atomics
- Tested with the WebAssembly specification test suite
- Written in (mostly) C89 and generates (mostly) C89 (some common extensions are used and not all limits are followed, see e.g. #98)
- Support for many operating systems (e.g. Mac OS X, Mac OS 9, Haiku, Rhapsody, OPENSTEP, NeXTSTEP, DOS, Windows XP, etc.)
- Support for many architectures (e.g. x86, ARM, PowerPC, SPARC, PA-RISC, etc.)
- Support for big-endian systems (e.g. PowerPC, SPARC, PA-RISC, etc.)
- Support for various compilers (e.g. old GCC, MSVC, CodeWarrior, etc.)
- Streaming/single-pass compilation, low memory usage
- Separate compilation into multiple files
- Parallel compilation
- Assist with incremental compilation: Separate static and dynamic functions by comparing with another module
- Support for multiple modules and instances
- Support for generating debug information:
- Function names, if function names are provided in the
namescustom section - Source line mapping, if DWARF line information is provided in the
.debug_linecustom section. Requires libdwarf to be installed. See instructions below. Line numbers outside C89's portable range of 1 through 32767 are omitted from the generated#linedirectives.
- Function names, if function names are provided in the
- WASI implementation
- Able to run clang and Python
- Support for many operating systems and architectures, support for big-endian systems
- Implements the threads proposal
- Coremark 1.0: ~7% slower than native
If your system is supported by at least CMake 2.8.12, prefer using CMake to detect features. On systems without CMake you can also use Make.
cd w2c2
cmake -B build
cmake --build buildcd wasi
cmake -B build
cmake --build buildw2c2 assumes valid WebAssembly input and does not perform full validation.
For example, to compile module.wasm to module.c (and module.h):
./w2c2 module.wasm module.cGenerated C that uses SIMD instructions or v128 types requires C99 and SIMDe.
Provide SIMDe separately and define W2C2_SIMDE_HEADER as the quoted path to its simde/wasm/simd128.h header when compiling the generated code:
cc -std=c99 -I/path/to/w2c2/w2c2 \
'-DW2C2_SIMDE_HEADER="/path/to/simde/simde/wasm/simd128.h"' \
-c module.cSIMDe is header-only, so no additional library is needed at link time. Building and running the translator does not require SIMDe. Non-SIMD output continues to support C89.
Define W2C2_RUNTIME_CHECKS=1 when compiling generated C to enable traps for
out-of-bounds memory and table accesses, unaligned atomics, and indirect-call signature mismatches.
The default, 0, omits these guards and their metadata to preserve the unchecked runtime's cost and layout.
Invalid operations in this mode can cause C undefined behavior.
Compile modules and host code that share runtime objects with the same setting.
w2c2 is able to compile a module into separate C files. This is recommended when compiling large modules and on hosts with limited resources.
For example, to compile module.wasm (and module.h), into multiple files with 100 functions each:
./w2c2 -f 100 module.wasm module.cWhen w2c2 was built with threading support, it is able to compile a module in parallel. By default, w2c2 spawns as many worker threads as CPU cores are available.
To manually specify the number of worker threads, pass the number using the -t flag.
For example, to compile using 2 threads:
./w2c2 -t 2 module.wasm module.cw2c2 builds a static translator library by default.
Use -DSHARED_LIB=ON with CMake,
or SHARED_LIB=1 with Make,
for a shared library on POSIX systems.
Windows supports static libraries only.
cmake -S w2c2 -B build -DCMAKE_INSTALL_PREFIX=/your/prefix
cmake --build build
ctest --test-dir build --output-on-failure
cmake --install buildCMake installs the library, public headers, CLI, and a relocatable package. Consumers can use:
find_package(w2c2 CONFIG REQUIRED)
target_link_libraries(my_translator PRIVATE w2c2::w2c2_lib)Pass -DCMAKE_PREFIX_PATH=/your/prefix when configuring the consumer.
The package supplies the library's thread and libdwarf dependencies when enabled.
With Make:
make -C w2c2
make -C w2c2 test
make -C w2c2 install PREFIX=/your/prefix
cc -I/your/prefix/include/w2c2 examples/embed/main.c /your/prefix/lib/libw2c2.a -lm -pthread -o embedMake installation supports DESTDIR for staging.
Add -ldwarf when the library was built with libdwarf support.
The CMake package is installed by CMake only.
Run make clean before changing build features or switching library types.
Coremark:
cd examples/coremark
make
./coremarkRunning the checked-in tests requires Python 3. Regenerating fixtures also requires WABT. See the test documentation for versions and extended-const coverage.
cd tests
make gen
make run-tests-
args_get -
args_sizes_get -
clock_res_get -
clock_time_get -
environ_get -
environ_sizes_get -
fd_advise -
fd_allocate -
fd_close -
fd_datasync -
fd_fdstat_get -
fd_fdstat_set_flags -
fd_fdstat_set_rights -
fd_filestat_get -
fd_filestat_set_size -
fd_filestat_set_times -
fd_pread -
fd_prestat_get -
fd_prestat_dir_name -
fd_pwrite -
fd_read -
fd_readdir -
fd_renumber -
fd_seek -
fd_sync -
fd_tell -
fd_write -
path_create_directory -
path_filestat_get -
path_filestat_set_times -
path_link -
path_open -
path_readlink -
path_remove_directory -
path_rename -
path_symlink -
path_unlink_file -
poll_oneoff -
proc_exit -
random_get -
sched_yield -
sock_recv -
sock_send -
sock_shutdown -
thread-spawn(from the threads proposal)
To build a debug release, pass BUILD=debug to make.
To enable sanitizers, list them in the SANITIZERS variable passed to make, e.g. make BUILD=debug SANITIZERS="base clang address thread".
baseenables the Undefined Behavior Sanitizerclangenables Clang-specific sanitizersthreadenables the Thread Sanitizeraddressenables the Address Sanitizer
-
On Linux, try installing a package named like
libdwarf-dev -
On macOS, you can use Homebrew and install
libdwarf(notdwarf!) -
w2c2 currently defaults to using the libdwarf API of >=v0.4.2. v0.6.0 has been tested to work successfully too.
-
If using a version <0.4.2, try passing
-DDWARF_OLD=1to CMake. Version 20201020 or later is required. -
Since version 0.1.1, libdwarf ships with a pkg-config file, which CMake should be able to detect automatically.
If libdwarf cannot be automatically found by CMake, you get the following message:
-- Checking for module 'libdwarf' -- No package 'libdwarf' foundIn that case you can still provide the necessary information manually by passing a variation of the following options:
-DDWARF_FOUND=1 -DDWARF_LIBRARIES=-ldwarf -DDWARF_LIBRARY_DIRS=/usr/lib -DDWARF_INCLUDE_DIRS=/usr/include/libdwarf