Skip to content
/ fizzy Public
forked from wasmx/fizzy

Fizzy aims to be a fast, deterministic, and pedantic WebAssembly interpreter written in C++.

License

Notifications You must be signed in to change notification settings

Qix-/fizzy

 
 

Repository files navigation

Fizzy

webassembly badge readme style standard badge circleci badge codecov badge license badge

Fizzy aims to be a fast, deterministic, and pedantic WebAssembly interpreter written in C++.

Goals

I) Code quality

  • Clean and modern C++17 codebase without external dependencies
  • Easily embeddable (and take part of the standardisation of the "C/C++ embedding API")

II) Simplicity

  • Only implement WebAssembly 1.0 (and none of the proposals)
  • Interpreter only
  • Support only WebAssembly binary encoding as an input (no support for the text format (.wat/.wast))

III) Conformance

  • Should have 99+% unit test coverage
  • Should pass the official WebAssembly test suite
  • Should have stricter tests than the official test suite

IV) First class support for determistic applications (blockchain)

  • Support canonical handling of floating point (i.e. NaNs stricter than in the spec)
  • Support an efficient big integer API (256-bit and perhaps 384-bit)
  • Support optional runtime metering in the interpreter
  • Support enforcing a call depth bound
  • Further restrictions of complexity (e.g. number of locals, number of function parameters, number of labels, etc.)

Building and using

Fizzy uses CMake as a build system:

$ mkdir build && cd build
$ cmake ..
$ cmake --build .

This will build Fizzy as a library. C API is provided for embedding 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.

WASI

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

Testing tools

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:

fizzy-bench

This can be used to run benchmarks available in the test/benchmarks directory. Read this guide for a short introduction.

fizzy-spectests

Fizzy is capable of executing the official WebAssembly tests. Follow this guide for using the tool.

fizzy-testfloat

A tool for testing implementations of floating-point instructions. Follow this guide for using the tool.

fizzy-unittests

This is the unit tests suite of Fizzy.

Special notes about floating point

Exceptions

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.

Rounding

The IEEE 754 standard defines four rounding directions (or modes):

  • to nearest, tie to even (default),
  • toward -∞ (rounding down),
  • toward +∞ (rounding up),
  • toward 0 (truncation).

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.

Development

Performance testing

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.

Releases

For a list of releases and changelog see the CHANGELOG file.

Maintainers

License

license badge

Licensed under the Apache License, Version 2.0.

About

Fizzy aims to be a fast, deterministic, and pedantic WebAssembly interpreter written in C++.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • C++ 84.5%
  • C 5.9%
  • Rust 4.4%
  • CMake 4.2%
  • WebAssembly 0.5%
  • Python 0.4%
  • Shell 0.1%