Go, lowered to machine code
marmot is a Go compiler built on MLIR and LLVM. Keep your Go code and your go.mod, switch the compiler, and get small, fast native binaries and errors that show you the fix.
Experimental: marmot compiles real Go programs, including itself, and checks every test program against Go, but it is not ready for production.
Go
Your code, unchanged.
var s int32for _, x := range xs {s += x}return sMLIR, with the go dialect
Still Go: an index into a slice, with its bounds check.
%v9 = llvm.extractvalue %a0[0] : !llvm.struct<(ptr, i64, i64)>%v10 = llvm.extractvalue %a0[1] : !llvm.struct<(ptr, i64, i64)>%v11 = go.index %v9[%v7] len %v10, i32 : i64%v12 = llvm.load %v11 {alignment = 4 : i64, go.unsafe} : !llvm.ptr -> i32%v13 = llvm.add %v1, %v12 : i32LLVM IR
The check is gone; four int32 values at a time.
%wide.load = load <4 x i32>, ptr %5, align 4%wide.load12 = load <4 x i32>, ptr %6, align 4%wide.load13 = load <4 x i32>, ptr %7, align 4%wide.load14 = load <4 x i32>, ptr %8, align 4%9 = add <4 x i32> %wide.load, %vec.phiarm64 machine code
Two 128 bit loads, four vector adds.
ldp q4, q5, [x8, #-0x20]ldp q6, q7, [x8], #0x40add.4s v0, v4, v0add.4s v1, v5, v1add.4s v2, v6, v2add.4s v3, v7, v3
Against go build
Measured on an Apple M3 Pro, newest measurement of each. Every program’s output is checked against Go. Shorter bars are better.
| Measurement | marmot | go | To scale |
|---|---|---|---|
hello world with println, -release binary size | 148 KB | 1.68 MB | |
hello world with fmt.Println, -release binary size | 665 KB | 2.4 MB | |
run time, 43 programs, -release geometric mean; 33 faster than Go, 10 slower | 1.6x faster | 1x | |
first build of cmd/marmot, empty caches 238 packages, 2026-11-13 10:46 | 5.13 to 5.43 s | 5.23 to 6.34 s | |
rebuild, nothing changed 238 packages, 2026-11-13 10:46 | 0.05 to 0.07 s | 0.14 to 0.15 s | |
one line edit, leaf package 238 packages, 2026-11-13 10:46 | 0.68 to 0.70 s | 0.50 to 0.51 s | |
one line edit, deep package 238 packages, 2026-11-13 10:46 | 0.94 to 0.96 s | 0.90 to 0.93 s |
Errors that show the fix
Marmots stand guard and whistle when something is wrong. marmot’s errors show the source, mark the exact span, label every place involved, and suggest a fix that marmot fix can apply.
Off a terminal they are the plain lines the go tool prints. Under a coding agent they are JSON, with the spans, the source around them and the fixes as edits. Every error has a stable code, and all 42 are explained.

One command for the whole loop
marmot works like the go command, and does what people usually install next to it.
- build
- Compile a program. Dev builds for fast rebuilds, -release for whole program optimization.
- run
- Compile and run a program.
- test
- Run package tests like go test, with each failure shown at its line.
- check
- Run vet, lint, gopls, vuln, tidy, cross builds and more in one cached pass.
- fix
- Apply the suggested fixes of errors and findings, and keep only what still builds.
- fmt
- Format source and fix imports, as gofmt and goimports do.
- watch
- Build, run or test again after every save.
- repl
- Evaluate Go expressions and statements as you type.
- bench
- Run benchmarks against go test and report which changes are significant.
- prof
- Read a pprof profile in the terminal, as a tree or line by line.
- size
- Show what a binary is made of, and why a function is in it.
- generate
- Run go:generate directives, restoring cached outputs.
- explain
- Describe an error code, with the mistake and the fix.
- doctor
- Check the installation and say what to fix.
- mcp
- Serve build, run, test, fix and explain to coding agents.
Go’s front end, MLIR and LLVM behind it
marmot keeps the parts of the Go toolchain that define the language: the type checker, the SSA builder and Go’s own standard library. It replaces the parts that generate code. A small go dialect keeps Go’s meaning in MLIR long enough to remove bounds checks, keep objects on the stack and call interface methods directly.
