(rontolisp) docs

Compile to a Native Executable

--native with -o compiles a program to ONE self-contained executable: the WASM output, precompiled to machine code by wasmtime and appended to a small runner. Running it needs neither wasmtime nor a JVM:

echo '(print (+ 1 2))' > hello.lisp
rontolisp hello.lisp --native -o hello
./hello
3

The executable is the WASI command module -o hello.wasm writes, run the way wasmtime run runs it: the same standard output, and the same exit status -- the program's own (uiop:quit n) code, 0 when it returns, 134 after an uncaught error. Its arguments are the program's (uiop:command-line-arguments) and it sees the process environment and standard input. Nothing else is written: the .wasm and its precompiled form stay in memory.

Files

The current directory and / are open to the program, so relative paths (../x included) and absolute paths work as they do in any native program.

HTTP

rontolisp:fetch works in an executable as it does on the interpreter and the JVM: the request is running when fetch returns, several requests overlap, await answers (:status :headers :body) with :body a stream of the reply's octets, and a request that fails (a refused connection, a certificate no trusted root vouches for) signals at the await. The runner makes the requests itself, over HTTP/1.1 and one connection each, and like the JDK's client it neither follows redirects nor decompresses.

HTTPS trusts Mozilla's root certificates, which are built into the executable, so it needs no certificate bundle on the machine. SSL_CERT_FILE names a PEM file of certificates to trust instead, for a private or TLS-inspecting certificate authority:

SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt ./fetcher

A program that fetches starts with a larger runner, the one that carries HTTP and TLS; every other program keeps the small one (Size and Speed).

Flags

Every flag of the default WASM output applies (--simd, --optimize, --dynamic, ...). The flags that ask for a different kind of module are refused by name: --component, --no-wasi, --no-gc, --host-random, --host-fetch, --host-boundary, --reentrant and --emit-js-glue. So is an -o name ending in .wasm, .class, .jar or .war. A program that fetches needs no flag: the runner answers fetch itself (HTTP).

Where It Runs

By default the executable is built for the platform the compiler runs on (Linux or macOS, x86_64 or aarch64) and uses only the CPU features every processor of that platform has, so it runs on any machine of the platform however old. On Linux it is statically linked, so it runs on any distribution of that architecture, whatever its C library. The released binaries and the executable JAR carry the precompiler; a build of rontolisp that carries none for the host says --native is not available for <os>-<arch> (building it from source: Build & Install).

--native-target builds for another platform: linux-x86_64, linux-aarch64 or macos-aarch64. The released binaries and the executable JAR carry the runner of each; a build from source carries its own platform's only, and names what it carries when asked for another.

rontolisp hello.lisp --native --native-target linux-aarch64 -o hello-arm64

--native-cpu chooses the CPU features the machine code may use: baseline (the default), host (every feature of the compiling machine's CPU), or on x86_64 a level, x86-64-v2, x86-64-v3 or x86-64-v4. An executable started on a CPU that lacks one of its features refuses to run and names the feature. Measured on the benchmark programs, the newer features did not make the code rontolisp generates faster, so the default costs nothing in practice.

The precompiler is a shared library that rontolisp extracts once to $XDG_CACHE_HOME/rontolisp (else ~/.cache/rontolisp, or ~/Library/Caches/rontolisp on macOS); the system property rontolisp.native.cache moves it (-Drontolisp.native.cache=DIR).

macOS GUI Programs

On Apple silicon (macos-aarch64) an executable also runs programs that use the objc, appkit, metal and scene packages: its runner calls the Objective-C runtime itself, and the program runs on the process's first thread, where AppKit wants it. See macOS GUI. For every other target such a program is a compile error naming the reference.

Size and Speed

An executable is a runner (2.1 MB on Linux x86_64, 1.9 MB on Linux aarch64, 1.7 MB on macOS) plus roughly 11 times the .wasm: 2.1 MB for hello on Linux x86_64, 2.3 MB for a 28 KB module. The runner of a program that fetches is 3.3 MB on Linux x86_64 and 2.9 MB on Linux aarch64. It starts in about 10 ms and runs at about the speed of wasmtime run on the same module.