Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Build options

sspur run and sspur test compile to native code by default: SSPUR emits C, compiles it with clang, and caches the result by content hash under ~/.cache/sspur/native/. The first run of a program pays for the build; later runs load the cached library. Each definition is its own cached object, so after an edit only the changed definitions and their callers are rebuilt. A plain sspur run file.ssp of a program that hasn’t changed since its last run (same source, same sspur binary, same SSPUR_*, CC and PATH) skips parsing, checking and code generation and runs the cached library’s main directly (ADR 0034). Anything else takes the normal path: any flag, a program inside a package, or one with warnings to print.

fn collatz(n: Int where _ > 0) -> Int ! div
= do
  var x = n
  var steps = 0
  while x != 1
    x := if x % 2 == 0 then x / 2 else 3 * x + 1
    steps := steps + 1
  steps

fn longest(limit: Int) -> Int ! div
= (1..limit).map(collatz).max.or(0)

fn main() -> Unit ! log, div
= log("longest chain under 100000: {longest(100000)} steps")
$ sspur run build.ssp
longest chain under 100000: 350 steps
$ sspur run --O3 --strict-native build.ssp
longest chain under 100000: 350 steps
$ sspur run --interp build.ssp
longest chain under 100000: 350 steps

Execution tiers

FlagEffect
(none)Native code at -O2 through clang, cached
--O3Native code at -O3, compiled as one unit
--interpThe reference interpreter. Every tier gives the same output and traps at the same point
--nativeThe Cranelift JIT tier
--strict-nativeFail if any function can’t be compiled natively, instead of warning and falling back
--quietHide the fallback warning

If a native build fails, SSPUR prints one warning naming the function and the C error, then falls back to the whole-program build or the interpreter, so a program always runs. --strict-native (or SSPUR_STRICT_NATIVE=1) makes that an error. CI runs with it, and so should anything that measures speed.

$ sspur run --pgo build.ssp
longest chain under 100000: 350 steps
...
$ sspur run --lto thin build.ssp
longest chain under 100000: 350 steps
FlagEffect
--pgoRun the program once instrumented, merge the profile with llvm-profdata, and build with it. The profile is cached per source
--retrainWith --pgo, repeat the training run
--lto off|thin|fullLink-time optimization across the per-definition objects. Default off. On Linux and Windows it needs lld

Both work with run, test and build. sspur run --profile FILE runs the interpreter and records call counts that guide inlining (ADR 0022), and sspur explain-opt FILE [--all] prints the proven rewrites applied before code generation.

Targets

CommandBuilds
sspur build --target riscv64-qemu FILE -o kernel.elfA profile bare kernel for QEMU virt, RISC-V machine mode
sspur build --target aarch64-qemu FILE -o kernel.elfA bare kernel for QEMU virt on Cortex-A53, with two cores
sspur build --target thumbv7em-mps2 FILE -o fw.elfCortex-M4 firmware for QEMU mps2-an386
sspur gpu FILE --emit metal|opencl|spirv|ptx [-o out]The kernel fns as Metal or OpenCL C source, or SPIR-V or PTX
sspur export-c FILE -o libfoo [--shared]A C static or shared library and its header
sspur deploy plan FILEA Lambda bootstrap.c, CloudFormation and IAM policies
sspur build --backend llvm FILE -o progThe direct LLVM IR prototype, for scalars, control flow and records

The bare-metal, GPU, C interop and CRUD service tutorials use these. Tools each target needs are listed under Installation.

Check and test options

OptionCommandsEffect
--jsoncheck, edit, apply, qMachine-readable output, with fix ops on diagnostics
--no-cachecheck, runcheck: recheck every definition instead of using the shared check cache. run: take the normal path even when the program is unchanged
--cases N, --seed N, --edgefuzzCases per function, the random seed (default 7), and a mix of extreme values such as the limits of Int and non-finite floats
--differentialfuzzCompare native code with the interpreter on every generated input
--writefmtRewrite the file in canonical form instead of printing it