Skip to content

Ecosystem

Eira edited this page Oct 31, 2026 · 7 revisions

Ecosystem

Projects that consume Lamina, and what building on it currently looks like.

Lamina is a backend, so what sits on top matters. Right now that is three projects at very different stages. Saying which is which is more use than a longer list would be.

Artery

A from-scratch Rust kernel and operating system, developed alongside Lamina by the same author. Its architecture names Lamina as a compiler backend and an LLVM alternative, held at arm's length: an external, versioned dependency the OS is never required to use, so that any language targeting Artery stays free to pick LLVM, GCC, or an interpreter instead.

For Lamina it is the most demanding target on the horizon. Freestanding, soft float, no red zone, no unwinding, script-driven layout, and a container binary format reached through an ELF converter. In a default build the artery OS name is not accepted at all. It is recognized only with the nightly feature. Nothing else is wired up.

Artery's own compiler plan reaches past being a target: a C subset frontend on Lamina, a frontend SDK with a stable IR builder and a diagnostics API, and eventually building user-space tools with Lamina and comparing the output against Clang and GCC.

See Artery target for the work items and what acceptance looks like.

brainfuck-lamina

A Brainfuck frontend, published as bfina. Source to AST to Lamina IR through IRBuilder, then out to assembly or an executable. It is the smallest complete example of the shape a frontend takes.

Treat it as a demonstration rather than a finished compiler. The implementation simulates the tape at compile time and executes loop bodies a fixed number of times, which is not general Brainfuck semantics, and its test claims have not been reconciled across the notes in the repository. It has still found real bugs in typed GEP and pointer handling.

Building a frontend on Lamina

The path all three take is the same:

  1. Parse your language into whatever AST suits it.
  2. Walk the AST and emit Lamina IR with IRBuilder, or generate IR text if that is easier to debug.
  3. Hand the module to Lamina for optimization and code generation.

IRBuilder covers modules, functions, basic blocks, instructions, types, memory, and control flow. From C, the same surface is available through the C bindings with opaque handles and explicit ownership.

Calling into C is supported. A function the module does not define is declared with @extern, and calls to it use the platform ABI:

@extern
fn @puts(ptr %s) -> i32

global $msg: [6 x i8] = "hello"

fn @main() -> i64 {
  entry:
    %p = getelem.i8 $msg, 0
    %r = call @puts(%p)
    ret.i64 0
}
$ lamina hello.lamina -o hello && ./hello
hello

Debug information is partly there. -g makes ras write a line table into the object file, and that table is correct. With the default weld link, the linked executable does not keep it, so a debugger sees no source lines. Linking through GNU ld with -c gnu keeps the line table:

$ readelf --debug-dump=decodedline hello_gnu
hello_extern.lamina                        8            0x40108c               x
hello_extern.lamina                        9            0x40108f               x
hello_extern.lamina                       10            0x40109d               x

Only line tables are emitted. There is no variable or type information yet. Fixing the weld path is open work under 0.3.

Other gaps a frontend author will meet:

  • 128-bit globals are rejected: global not yet supported: @big is a 128-bit integer.
  • Aggregates passed or returned by value depend on the target. x86_64 handles them against the C ABI. Other backends reject some cases by name. See 0.1.
  • 64-bit integers do not work on every 32-bit target. wasm32 rejects i64 parameters and returns. i686 returns one in %edx:%eax and divides through the compiler runtime. riscv32_unknown rejects 64-bit division, because it is freestanding and a freestanding target links no compiler runtime; the routine exists, nothing puts it on the link line.
  • 128-bit and 64-bit arithmetic that has no single instruction calls Lamina's own compiler runtime, which the driver compiles for the target and links for you. You do not have to supply __divti3 or __divdi3, except on a freestanding target, where nothing is linked that the module did not ask for.

Neither is a reason not to start. Know them before you do.

Adding to this page

If you build something on Lamina, open an issue on SkuldNorniern/lamina and it can go here. A short description of what it does and which parts of Lamina it leans on is enough.

Clone this wiki locally