systems programming language

mach

Mach is a small, explicit systems language for compilers, kernels, runtimes and games: anywhere performance is a requirement and hidden behavior is a liability.

>curl -fsSL https://machlang.org/install.sh | sh
>curl -fsSL https://machlang.org/install.sh | sh
>irm https://machlang.org/install.ps1 | iex

run it in powershell

installs the latest release to ~/.local/bin or %LOCALAPPDATA%\mach\bin

principles

CH1

No hidden control flow

No exceptions, no destructors, no overloaded operators. Execution goes where the code says and nowhere else.

CH2

No hidden allocation

No garbage collector and no runtime quietly reaching for the heap. Memory moves when you move it.

CH3

Types on the page

Nothing is inferred. Every binding states its type, so a reader never has to reconstruct what the compiler decided.

CH4

One toolchain

A single binary builds, links, tests, formats, vendors dependencies and cross-compiles. Nothing else to install.

a closer look · examples/alloc.mach

Nothing happens off the page.

Read a Mach program top to bottom and you have read everything it does. The allocator is a value it holds, each allocation names its type, each failure comes back as a value it checks, and each free is written where it happens. Not even the runtime is out of sight: it is a standard library module the program imports, not something the compiler slips in.

  1. the runtime is a std module you import, not part of the compiler
  2. main is exported by name, for the runtime's _start to call
  3. every binding states its type
  4. the allocator is a value you hold
  5. each allocation names its allocator and its type
  6. failure is a value you check, never thrown
  7. reachable because the error branch returned
  8. freed by the same allocator, on the page

what it's for

01

Operating systems, firmware and bootloaders

Write the kernel, the bootloader and the build tools in one language. Freestanding targets and assembly live in the same source as everything else.

inline assembly
mach.toml
[target.boot]
isa = "x86_64"
os  = "freestanding"
abi = "sysv64"
of  = "elf"         # loadable by limine or grub
src/cpu.mach
pub fun halt() {
    asm x86_64 {
        cli
        hlt
    }
}
02

Constant-time cryptography

Mark a value secret and the compiler refuses to let it steer a branch, an index or a timing. Constant-time code, checked instead of hoped for.

experimental preview secrecy
src/mac.mach
#[oblivious]
fun check(mac: ^u64, want: ^u64) bool {
    if (mac == want) {
        ret true;
    }
    ret false;
}
error: secret value used as a branch condition
  --> ./src/mac.mach:3:5
03

GPU compute and shaders

Vertex, fragment and compute stages are Mach functions. A spirv target compiles them straight to a finished SPIR-V module a Vulkan driver loads, with no shading language on the side and no second toolchain.

gpu shaders
src/sim.mach
rec Particles {
    position: [64]f32x4;
    velocity: [64]f32x4;
}

#[storage(0, 0)]
var particles: Particles;

#[builtin("global_invocation")]
var global_id: u32x3;

#[stage("compute")]
#[workgroup(64, 1, 1)]
fun step() {
    val i: u32 = global_id[0];
    particles.position[i] = particles.position[i] + particles.velocity[i];
}
mach.toml
[target.gpu]
isa = "spirv"
os  = "freestanding"
abi = "spirv"
env = "vulkan1.3"

targets

One source, every target.

A target is a fully spelled tuple of isa, os and abi, plus the extensions every host it runs on is promised to have. Declare them once in mach.toml and one command builds every cell, from a desktop binary to a bare-metal image to a GPU module.

mach.toml
[target.linux]
isa = "x86_64"
os  = "linux"
abi = "sysv64"
extensions = ["x86-64-v3", "sha"]

[target.mac]
isa = "aarch64"
os  = "darwin"
abi = "aapcs64"

[target.win]
isa = "x86_64"
os  = "windows"
abi = "win64"

[target.board]
isa = "rv32imc"
os  = "freestanding"
abi = "ilp32"

[target.gpu]
isa = "spirv"
os  = "freestanding"
abi = "spirv"
env = "vulkan1.3"
>mach build . --all
src/ one source linux.elf x86_64 · x86-64-v3 + sha mac.macho aarch64 · darwin win.coff x86_64 · windows board.raw rv32imc · bare metal gpu.spv spirv · vulkan1.3
src/sha.mach · the target's extensions are comptime facts
$if ($mach.build.extensions.sha) {
    use impl: app.sha.hw;
}
$or {
    use impl: app.sha.portable;
}

$if gates compose code per target. The branch a target does not select is dropped before it is type-checked, so a portable fallback and a hardware path share one module.

isa
x86_64aarch64riscv64riscv32rv32imc, rv64imafd, …spirvwasm32
os
linuxdarwinwindowsfreestandingwasi
abi
sysv64win64aapcs64lp64lp64flp64dilp32ilp32filp32dspirv
format
elfcoffmachorawspvwasm
extensions
x86-64-v2x86-64-v3x86-64-v4shaavx2avx512f…sha2m a f d czicsrzifenceizkt
spir-v env
vulkan1.0vulkan1.1vulkan1.2vulkan1.3

x86_64 linux is the primary host. aarch64 linux runs natively in CI, riscv64 linux self-hosts under qemu, darwin self-hosts on both architectures, and windows is a cross-compilation target. mach info targets lists every tuple your binary can build. Dashed values are in progress.

self-hosted

Written in Mach.

The compiler, code generators and linker are Mach all the way through, with no external dependencies. The language is proven on the hardest program it will ever build: itself.

  1. machlast release
  2. stage 1
  3. stage 2
  4. stage 3

stage 2 ≡ stage 3 · fixpoint

Each generation compiles the next from the same source. When two in a row come out the same, the compiler has reproduced itself.

start

Getting started

  1. install
    >curl -fsSL https://machlang.org/install.sh | sh
    >curl -fsSL https://machlang.org/install.sh | sh
    >irm https://machlang.org/install.ps1 | iex
  2. create a project and step into it
    >mach init hello
    >cd hello
  3. build it
    >mach build .
  4. run it
    >mach run .
hello/src/main.mach

Next: the documentation (a pamphlet, not a bible), or start from mach-sieve.