principles
No hidden control flow
No exceptions, no destructors, no overloaded operators. Execution goes where the code says and nowhere else.
No hidden allocation
No garbage collector and no runtime quietly reaching for the heap. Memory moves when you move it.
Types on the page
Nothing is inferred. Every binding states its type, so a reader never has to reconstruct what the compiler decided.
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.
- the runtime is a std module you import, not part of the compiler
- main is exported by name, for the runtime's _start to call
- every binding states its type
- the allocator is a value you hold
- each allocation names its allocator and its type
- failure is a value you check, never thrown
- reachable because the error branch returned
- freed by the same allocator, on the page
what it's for
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[target.boot]
isa = "x86_64"
os = "freestanding"
abi = "sysv64"
of = "elf" # loadable by limine or grub
pub fun halt() {
asm x86_64 {
cli
hlt
}
}
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#[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
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 shadersrec 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];
}
[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.
[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$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.
- machlast release
- stage 1
- stage 2
- 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
-
install>
curl -fsSL https://machlang.org/install.sh | sh>curl -fsSL https://machlang.org/install.sh | sh>irm https://machlang.org/install.ps1 | iex -
create a project and step into it
>
mach init hello>cd hello - build it>
mach build . - run it>
mach run .
Next: the documentation (a pamphlet, not a bible), or start from mach-sieve.