A from-scratch C library for WebAssembly. The name comes from Gary Bernhardt's The Birth & Death of JavaScript — a vision of "metal" web applications running at near-native speed. Metallic aims to be the runtime for that world.
# Build the libraries (requires clang with the wasm32 target and llvm-link).
make
# Run the included hello-world example under wasmtime.
cd examples/hello && make run
Metallic builds to a single static archive, metallic.a, that calls
WASI snapshot_preview1 directly. Programs link it like any other library:
clang --target=wasm32-unknown-unknown-wasm -nostdlib \
-I include main.c metallic.a -o app.out
Host I/O lives in src/wasi/wasi.h, where each binding
carries the import_module("wasi_snapshot_preview1") attribute so the
linker emits them as wasm imports without needing --allow-undefined.
Implemented C11 hosted-environment headers:
<assert.h><complex.h><ctype.h><errno.h><fenv.h><float.h><inttypes.h><iso646.h><limits.h><locale.h><math.h><setjmp.h><signal.h><stdalign.h><stdarg.h><stdbool.h><stddef.h><stdint.h><stdio.h><stdlib.h><stdnoreturn.h><string.h><tgmath.h><time.h><uchar.h><wchar.h><wctype.h>
Opted out via __STDC_NO_THREADS__: <threads.h> is intentionally not
shipped. <stdatomic.h> comes from clang's freestanding headers and
lowers to plain operations on single-threaded WASI.
Correct rounding is the math library's goal, and most transcendentals reach it.
A function is correctly rounded when its result is the true value rounded to
the nearest representable number (error ≤ 0.5 ulp); faithfully rounded is the
weaker guarantee of one of the two nearest (error < 1 ulp). For float, correct
rounding is proven by an exhaustive sweep of all 2³² bit patterns against an
MPFR or CORE-MATH oracle; for double it is strong evidence from a sampler that
hammers the published hard-to-round cases plus a broad random sample. See
test/oracle/ and make check.oracle (native; not part of the wasm CI gate).
float, correctly rounded (≤ 0.5 ulp). Unary, proven by exhaustive sweep:expfexp2fexpm1flogflog2flog10flog1pfsinfcosftanfasinfacosfatanfasinhfacoshfatanhfsinhfcoshftanhfcbrtferfferfcflgammaftgammaf. Bivariate (sampler evidence, since the 2⁶⁴ domain cannot be swept):atan2fhypotfpowf.double, correctly rounded (≤ 0.5 ulp), sampler evidence.expexp2expm1loglog2log10log1pcbrtatanasinacoshypotasinhacoshatanherf.double, faithfully rounded (< 1 ulp).sincostansinhcoshtanhatan2erfclgammatgamma— pending a wider accurate path.- Complex
float, correctly rounded (≤ 0.5 ulp per part), sampler evidence. Each of the real and imaginary parts is the correctly-roundedfloatof the exact value (the 2⁶⁴ domain cannot be swept, so this is a random + near-axis MPFR sampler, as for the bivariate floats):cabsfcargfcsqrtfcexpfclogf. Seetest/oracle/math/float/complex/andmake check.oracle.complex. The remaining complexfloatfunctions (the trig/hyperbolic and inverse families,cpowf) are still composed from the real kernels and faithful. - Complex
double(cabs,cexp,clog,csqrt, the trig/hyperbolic families, …) is composed from the real kernels and inherits their accuracy.
longjmpaborts the process. WebAssembly has no native stack unwinding; programs that need real setjmp/longjmp should compile with clang's-mllvm -wasm-enable-sjlj, which lowerssetjmp/longjmpto clang intrinsics and bypasses libc.signal/raisetrack handlers but do not asynchronously dispatch — WASI preview1 delivers no signals.raise(SIGABRT)is correctly routed.localtimealiasesgmtime— WASI preview1 has no timezone info.- Only the
Clocale is supported. - No threads.
__STDC_NO_THREADS__is predefined, so<threads.h>is absent and feature-test code routes around the C11 thread API. Atomics use clang's freestanding<stdatomic.h>; on single-threaded WASI they lower to plain loads and stores.
make check.native— native tests (some pre-existing math accuracy failures against the host libm).make check.wasm.fast— wasm tests underwasmtime, excluding the 11 pre-existing soft-float/integer/long-double failures. CI runs this.make check.wasm— full wasm suite including the known-broken tests.
If wasmtime is installed but not on PATH (for example via
$HOME/.wasmtime/bin/wasmtime), the Makefile will use that location
automatically. You can also select another runner explicitly:
make WASMRUN=/path/to/wasmtime check.wasm.fast
11 pre-existing test failures in the soft-float / 128-bit integer / long-double
sqrt code paths. These are not regressions and are excluded from
check.wasm.fast. See Makefile KNOWN_BROKEN_WASM.
This was originally Chen-Pang He's libc-for-WebAssembly experiment. Revival work brought it up to a hosted C11 + WASI snapshot_preview1 runtime; the math kernels predate the revival.
See LICENSE.