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.

  1. Go

    Your code, unchanged.

    var s int32
    for _, x := range xs {
    s += x
    }
    return s
  2. MLIR, 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 : i32
  3. LLVM 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.phi
  4. arm64 machine code

    Two 128 bit loads, four vector adds.

    ldp q4, q5, [x8, #-0x20]
    ldp q6, q7, [x8], #0x40
    add.4s v0, v4, v0
    add.4s v1, v5, v1
    add.4s v2, v6, v2
    add.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.

MeasurementmarmotgoTo scale
hello world with println, -release
binary size
148 KB1.68 MB
hello world with fmt.Println, -release
binary size
665 KB2.4 MB
run time, 43 programs, -release
geometric mean; 33 faster than Go, 10 slower
1.6x faster1x
first build of cmd/marmot, empty caches
238 packages, 2026-11-13 10:46
5.13 to 5.43 s5.23 to 6.34 s
rebuild, nothing changed
238 packages, 2026-11-13 10:46
0.05 to 0.07 s0.14 to 0.15 s
one line edit, leaf package
238 packages, 2026-11-13 10:46
0.68 to 0.70 s0.50 to 0.51 s
one line edit, deep package
238 packages, 2026-11-13 10:46
0.94 to 0.96 s0.90 to 0.93 s

Every number and how it was measured

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.

shapes
marmot build: error M0014, Square does not implement Shape, with labels on the interface and on the method with a pointer receiver, and a fix that adds &

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.

Go sourceType check and SSAMLIRGo-aware passesLLVMNative binaryRuntime: GC, scheduler
How marmot is put together
A marmot is a close relative of the gopher, and it starts with M, like MLIR.Source on GitHub