Fizzy aims to be a fast, deterministic, and pedantic WebAssembly interpreter written in C++.
Fizzy aims to be a fast, deterministic, and pedantic WebAssembly interpreter written in C++.
I) Code quality
II) Simplicity
.wat
/.wast
))III) Conformance
IV) First class support for determistic applications (blockchain)
Fizzy uses CMake as a build system. This will build Fizzy as a library:
$ mkdir build && cd build
$ cmake ..
$ cmake --build .
A public C API is provided for embedding the Fizzy engine in applications.
A small example of using the C API, which loads a wasm binary and executes a function named main
:
#include <fizzy/fizzy.h>
bool execute_main(const uint8_t* wasm_binary, size_t wasm_binary_size)
{
// Parse and validate binary module.
const FizzyModule* module = fizzy_parse(wasm_binary, wasm_binary_size);
// Find main function.
uint32_t main_fn_index;
if (!fizzy_find_exported_function_index(module, "main", &main_fn_index))
return false;
// Instantiate module without imports.
FizzyInstance* instance = fizzy_instantiate(module, NULL, 0, NULL, NULL, NULL, 0);
// Execute main function without arguments.
const FizzyExecutionResult result = fizzy_execute(instance, main_fn_index, NULL, 0);
if (result.trapped)
return false;
fizzy_free_instance(instance);
return true;
}
Fizzy also provides a CMake package for easy integration in projects that use it:
find_package(fizzy CONFIG REQUIRED)
...
target_link_libraries(app_name PRIVATE fizzy::fizzy)
Fizzy also has a Rust binding. It is published on crates.io and the
official documentation with examples can be read on docs.rs.
Building with the FIZZY_WASI
option will output a fizzy-wasi
binary implementing
the WASI API (a very limited subset of wasi_snapshot_preview1, to be precise).
It uses uvwasi under the hood. It can be used to execute WASI-compatible binaries on the command line.
$ mkdir build && cd build
$ cmake -DFIZZY_WASI=ON ..
$ cmake --build .
Try it with a Hello World example:
$ bin/fizzy-wasi ../test/smoketests/wasi/helloworld.wasm
hello world
Building with the FIZZY_TESTING
option will output a few useful utilities:
$ mkdir build && cd build
$ cmake -DFIZZY_TESTING=ON ..
$ cmake --build .
These utilities are as follows:
This can be used to run benchmarks available in the test/benchmarks
directory.
Read this guide for a short introduction.
Fizzy is capable of executing the official WebAssembly tests.
Follow this guide for using the tool.
A tool for testing implementations of floating-point instructions.
Follow this guide for using the tool.
This is the unit tests suite of Fizzy.
Certain floating point operations can emit exceptions as defined by the IEEE 754 standard.
(Here is a summary from the GNU C Library).
It is however up to the language how these manifest, and in C/C++ depending on the settings they can result in
a) setting of a flag; b) or in traps, such as the SIGFPE
signal.
Fizzy does not manipulate this setting, but expects it to be left at option a) of above, which is the default.
In the GNU C Library this can be controlled via the feenableexcept
and fedisableexcept
functions.
The user of the Fizzy library has to ensure this is not set to trap mode.
The behavior with traps enabled is unpredictable and correct behaviour is not guaranteed, thus we strongly advise against using them.
The IEEE 754 standard defines four rounding directions (or modes):
The WebAssembly specification expects the default, “to nearest”, rounding mode for instructions
whose result can be influenced by rounding.
Fizzy does not manipulate this setting, and expects the same rounding mode as WebAssembly,
otherwise the result of some instructions may be different.
In the GNU C Library the rounding mode can be controlled via the fesetround
and fegetround
functions.
If strict compliance is sought with WebAssembly, then the user of Fizzy must ensure to keep the default rounding mode.
On the 32-bit Intel i386 architecture an x87-compatible FPU is used by default to perform floating-point operations.
The FPU is claimed to be IEEE-754 compliant, but there is one gotcha. The operations are executed with so-called internal precision and the results are rounded to the target precision at the end [1].
By default, the precision is set to 80-bit extended precision (except for VC++ runtime [2]).
Unfortunately, this causes problems for 64-bit double precision operations (f64.add
, f64.sub
, f64.mul
, f64.div
) as the results may be different from when computed with double precision directly.
The FPU precision can be dynamically modified by using compiler intrinsics [1], but this has similar issues to controlling the rounding mode and there exists no C/C++ standard way of doing so.
We decided against fighting the x87 FPU quirks, because floating-point operations were not the top priorities.
Instead of creating manual workarounds, a reasonable solution is to opt-in for using SSE2 instructions to implement WebAssembly floating-point instructions,
not only for 64-bit (where it is the default), but 32-bit builds as well. This means for strict WebAssembly compliance the SSE2 instruction set is required.
This is controlled by the -msse2 -mfpmath=sse
compiler options, and one can always override to experiment with the x87 FPU.
Worth mentioning that the -mpc64
GCC compiler option is supposed to set the FPU to 64-bit double precision mode, but for an unknown reason this is not working.
See also:
We use the following build configuration for performance testing:
Release build type (-DCMAKE_BUILD_TYPE=Release
)
This enables -O3
optimization level and disables asserts.
Link-Time Optimization (-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=TRUE
)
This does not give big performance gains (v0.3: +1%, v0.4: -3%), but most importantly
eliminates performance instabilities related to layout changes in the code section of the binaries.
See #510 for example.
Latest version of GCC or Clang compiler
The Fizzy built with recent versions of GCC and Clang has comparable performance.
For LTO builds of Fizzy 0.4.0 Clang 10/11 is 4% faster than GCC 10.
libstdc++ standard library
No “native” architecture selection
The compiler option -march=native
is not used, leaving default architecture to be selected.
Building for native CPU architecture can be easily enabled with CMake option -DNATIVE=TRUE
.
We leave the investigation of the impact of this for the future.
For a list of releases and changelog see the CHANGELOG file.
Licensed under the Apache License, Version 2.0.