.profraw next to itself
--release release build: whole program optimization, -O3, all specializations (default from $MARMOT_RELEASE=1)
--s omit the pc table (tracebacks without file:line)
--tags string comma separated build tags
--target string target triple (default: host)
--trace string traceback detail of -release builds: full keeps the inlined frames of each call site apart (as dev builds do)
--v print progress and timing
--work print and keep the temporary work directory
--x print the commands run
```
# marmot size (/docs/commands/size)
Break an executable down by section, by package and by function, and show what calls a function (-why). Function names and sizes come from marmot's pc table, or from the symbol table for other binaries.
```text title="Usage"
marmot size BINARY [flags]
```
## Examples [#examples]
```sh
marmot size ./app
marmot size -pkg fmt ./app
marmot size -why fmt.Sprintf ./app
```
`marmot size app` lists the sections of the executable, the code of each package and the
largest functions. `-pkg fmt` lists one package's functions; `-why fmt.Sprintf` prints a chain
of direct calls from `main` to the function, which says why it is linked in. It also reads
binaries built by other compilers, through their symbol table.
`-json` writes one `size` object with `sections`, `packages` and `functions`; with `-why` it
has `why` with `chain`.
## Flags [#flags]
Flags take one dash or two: `-release` and `--release` are the same.
| Flag | Meaning |
| --- | --- |
| `-color string` | color in human output: auto, always or never (NO_COLOR turns auto off) (default "auto") |
| `-json` | machine readable output: JSON Lines on stderr (also $MARMOT_OUTPUT=json, and the default under coding agents) |
| `-n string` | how many packages and functions to list (default "15") |
| `-pkg string` | list the functions of this package only |
| `-why string` | show a chain of direct calls from main to this function |
## Full help [#full-help]
```text title="marmot size --help"
Break an executable down by section, by package and by function, and show what calls a function (-why). Function names and sizes come from marmot's pc table, or from the symbol table for other binaries.
Usage:
marmot size BINARY [flags]
Examples:
marmot size ./app
marmot size -pkg fmt ./app
marmot size -why fmt.Sprintf ./app
Flags:
--color string color in human output: auto, always or never (NO_COLOR turns auto off) (default "auto")
-h, --help help for size
--json machine readable output: JSON Lines on stderr (also $MARMOT_OUTPUT=json, and the default under coding agents)
--n string how many packages and functions to list (default "15")
--pkg string list the functions of this package only
--why string show a chain of direct calls from main to this function
```
# marmot test (/docs/commands/test)
Build the test binary of each package and run it, like go test. Test flags (-run, -v, -count, -bench, ...) go to the test binary. With -json, every test and failure is reported as JSON Lines.
```text title="Usage"
marmot test [packages] [flags]
```
## Examples [#examples]
```sh
marmot test ./...
marmot test -run TestParse -v .
```
On a terminal `marmot test` shows each failure first, at the line of its `t.Error` or
`t.Fatal` with the source, then every test and subtest with its status and time, then a
summary. Off a terminal the output is the same as `go test`, so CI scripts that parse it keep
working. `marmot test -watch` runs the tests again after every change.
`marmot test -json` writes one `{"kind":"test", "package", "name", "status", "durationMs",
"file", "line", "message", "output"}` line per test and subtest, a `diagnostic` (code
[M0022](/docs/diagnostics/codes/m0022)) per failure with its source context, and the result.
## Flags [#flags]
Flags take one dash or two: `-release` and `--release` are the same.
| Flag | Meaning |
| --- | --- |
| `-O string` | optimization level: 0, 1, 2, 3, s, z |
| `-color string` | color in human output: auto, always or never (NO_COLOR turns auto off) (default "auto") |
| `-dialect string` | MLIR dialect level: llvm or go |
| `-json` | machine readable output: JSON Lines on stderr (also $MARMOT_OUTPUT=json, and the default under coding agents) |
| `-o string` | write the test binary here and do not run it |
| `-release` | release build |
| `-tags string` | comma separated build tags |
| `-work` | print and keep the temporary work directory |
| `-x` | print the commands run |
## Full help [#full-help]
```text title="marmot test --help"
Build the test binary of each package and run it, like go test. Test flags (-run, -v, -count, -bench, ...) go to the test binary. With -json, every test and failure is reported as JSON Lines.
Usage:
marmot test [packages] [flags]
Examples:
marmot test ./...
marmot test -run TestParse -v .
Flags:
--O string optimization level: 0, 1, 2, 3, s, z
--color string color in human output: auto, always or never (NO_COLOR turns auto off) (default "auto")
--dialect string MLIR dialect level: llvm or go
-h, --help help for test
--json machine readable output: JSON Lines on stderr (also $MARMOT_OUTPUT=json, and the default under coding agents)
--o string write the test binary here and do not run it
--release release build
--tags string comma separated build tags
--work print and keep the temporary work directory
--x print the commands run
```
# marmot watch (/docs/commands/watch)
Build now and after every change to a .go file, go.mod or go.sum of the module, using the build cache so only what changed is compiled again. With run, the program is stopped and started again; with test, the tests run again. The screen is cleared before each round on a terminal. Watch flags (-interval, -clear) come first; the other flags go to the build.
```text title="Usage"
marmot watch [build|run|test] [packages] [flags]
```
## Examples [#examples]
```sh
marmot watch
marmot watch run ./cmd/server -port 8080
marmot watch test -run TestParse ./...
```
`marmot watch` builds the current package now and again after every change to a `.go` file,
`go.mod` or `go.sum`. `marmot watch run ./cmd/server -port 8080` stops the program and starts
the new build after each change; `marmot watch test ./...` runs the tests again.
The screen is cleared before each round on a terminal (`-clear=false` keeps the scrollback);
`-interval` sets how often files are checked. Rebuilds go through the build cache, so only what
changed is compiled again. `marmot watch -generate` runs [marmot generate](/docs/commands/generate)
before every build.
## Flags [#flags]
Flags take one dash or two: `-release` and `--release` are the same.
| Flag | Meaning |
| --- | --- |
| `-clear` | clear the screen before each build (terminal only) (default true) |
| `-color string` | color in human output: auto, always or never (NO_COLOR turns auto off) (default "auto") |
| `-generate` | run marmot generate (cached) before each build |
| `-interval string` | how often to look for changed files (default "300ms") |
| `-json` | machine readable output: JSON Lines on stderr (also $MARMOT_OUTPUT=json, and the default under coding agents) |
## Full help [#full-help]
```text title="marmot watch --help"
Build now and after every change to a .go file, go.mod or go.sum of the module, using the build cache so only what changed is compiled again. With run, the program is stopped and started again; with test, the tests run again. The screen is cleared before each round on a terminal. Watch flags (-interval, -clear) come first; the other flags go to the build.
Usage:
marmot watch [build|run|test] [packages] [flags]
Examples:
marmot watch
marmot watch run ./cmd/server -port 8080
marmot watch test -run TestParse ./...
Flags:
--clear clear the screen before each build (terminal only) (default true)
--color string color in human output: auto, always or never (NO_COLOR turns auto off) (default "auto")
--generate run marmot generate (cached) before each build
-h, --help help for watch
--interval string how often to look for changed files (default "300ms")
--json machine readable output: JSON Lines on stderr (also $MARMOT_OUTPUT=json, and the default under coding agents)
```
# Design (/docs/design)
marmot reuses the parts of the Go toolchain that define the language, and replaces the parts
that generate code.
## Pipeline [#pipeline]
1. **Front end.** The standard `go/types` checker and the `x/tools` SSA builder turn source
into a simple, typed instruction form. Generic code is expanded here.
2. **MLIR.** Each package becomes an MLIR module. Plain operations use MLIR's `llvm` dialect.
Operations that carry Go meaning use a small `go` dialect.
3. **Go-aware passes.** These run while the program still says "allocate a slice" or "call
through an interface", which makes some optimizations much easier than on LLVM IR.
4. **LLVM.** The module is lowered to LLVM IR, optimized, and turned into machine code. All of
this happens inside one backend process.
5. **Link.** Objects are linked with the runtime by the system linker. Unused code is removed.
## What lowering looks like [#what-lowering-looks-like]
One function, captured at each level with `marmot build -emit mlir`, `-emit llvm` and `-emit obj`.
The marked parts are what changed at that level.
```go title="Go"
// [!code word:range xs]
func Sum(xs []int32) int32 {
var s int32
for _, x := range xs {
s += x
}
return s
}
```
In MLIR the loop still says what Go means: `go.index` is an index into a slice, with its bounds
check.
```text title="MLIR, with the go dialect"
// [!code word:go.index]
%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
```
In LLVM IR at `-O 2` the check is gone and the loop works on four `int32` values at a time.
```text title="LLVM IR"
// [!code word:<4 x i32>]
%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
```
On arm64 that is two 128 bit loads and four vector adds each time round.
```text title="arm64"
// [!code word:add.4s]
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
```
## Why a Go dialect [#why-a-go-dialect]
LLVM sees memory, branches and calls. It does not know that a branch is a bounds check, or that
a call allocates. The `go` dialect keeps that knowledge long enough to use it.
| Group | Examples | What the passes do with it |
| --- | --- | --- |
| Allocation | `new`, `make`, closures, boxing | Keep objects on the stack when they do not outlive the function. |
| Indexing | index, slice, bounds and nil checks | Remove checks that can be shown never to fail. |
| Interfaces | interface calls, type assertions | Call the method directly when the type is known. |
| Control | `defer`, `panic`, `go` | Make `defer` cheap when no panic can reach it. |
| Strings | concatenation, conversions | Join concatenations, avoid copies when safe. |
| Loops | counted loops over slices | Vectorize, and run independent iterations in parallel. |
## Build modes [#build-modes]
In dev mode each package is compiled separately and cached, so an edit recompiles little. In
release mode the optimizer sees the whole program, which allows inlining across packages and
removal of code that nothing uses. See [Build modes](/docs/build-modes).
## Runtime [#runtime]
Every binary carries a small runtime in two layers.
| Part | How it works |
| --- | --- |
| Garbage collector | Mark and sweep. It does not move objects. Marking runs on several threads. |
| Goroutines | Each has its own stack. Many goroutines share a few OS threads, and idle threads take work from busy ones. |
| Channels | Unbuffered channels hand values directly to the waiting goroutine. Buffered channels use a lock-free ring. |
| Maps | Open addressing in groups, with fast paths for integer and string keys. |
| Panics | `defer`, `panic` and `recover` follow Go's rules, including stack traces with file and line. |
| Library fast paths | Byte search, UTF-8, hex, base64, CRC and SHA-2 use vector instructions where the CPU has them. |
The standard library is Go's own source. marmot replaces only the packages that are tied to
Go's runtime or written in assembly.
## Keeping it correct [#keeping-it-correct]
A fast compiler is only useful if its output is right. marmot is checked in three ways.
- **Comparison with Go.** A suite of several hundred programs is built with both compilers,
and the output and exit status must match. A fuzzer generates more programs at random.
- **Review of every optimization.** Each optimization is switched on by default only after its
rule is written down and it passes the whole suite with the optimization forced on.
- **Proofs.** The rules behind the riskier optimizations (bounds check removal, loop
parallelization, object lifetimes) are proved in Bend 2 and Lean 4, and a separate checker
re-examines what the compiler actually did.
## Repository layout [#repository-layout]
| Path | Contents |
| --- | --- |
| `cmd/marmot` | The command line tool. |
| `internal/` | Front end: loading, code generation to MLIR, build driver. |
| `backend/` | C++ backend: the `go` dialect, passes, LLVM lowering. |
| `runtime/core/` | C part of the runtime. |
| `lib/src/` | Go part of the runtime and replaced standard library packages. |
| `tests/` | Conformance programs and the tools that run them. |
| `bench/` | Benchmark programs and runner. |
| `verify/` | Proofs and checkers. |
# Error codes (/docs/diagnostics/codes)
Every diagnostic has a stable code. `marmot explain CODE` prints the same description in the terminal, and `marmot explain -json CODE` as JSON.
## Build errors [#build-errors]
| Code | What it means |
| --- | --- |
| [M0000](/docs/diagnostics/codes/m0000) | error |
| [M0001](/docs/diagnostics/codes/m0001) | syntax error |
| [M0002](/docs/diagnostics/codes/m0002) | undefined name |
| [M0003](/docs/diagnostics/codes/m0003) | no such field or method |
| [M0004](/docs/diagnostics/codes/m0004) | declared and not used |
| [M0005](/docs/diagnostics/codes/m0005) | imported and not used |
| [M0006](/docs/diagnostics/codes/m0006) | type mismatch |
| [M0007](/docs/diagnostics/codes/m0007) | mismatched types |
| [M0008](/docs/diagnostics/codes/m0008) | wrong number of return values |
| [M0009](/docs/diagnostics/codes/m0009) | wrong number of arguments |
| [M0010](/docs/diagnostics/codes/m0010) | missing return |
| [M0011](/docs/diagnostics/codes/m0011) | package not found |
| [M0012](/docs/diagnostics/codes/m0012) | assignment count mismatch |
| [M0013](/docs/diagnostics/codes/m0013) | no new variables |
| [M0014](/docs/diagnostics/codes/m0014) | does not implement interface |
| [M0015](/docs/diagnostics/codes/m0015) | redeclared |
| [M0016](/docs/diagnostics/codes/m0016) | invalid operation |
| [M0017](/docs/diagnostics/codes/m0017) | bad command line |
| [M0018](/docs/diagnostics/codes/m0018) | link error |
| [M0019](/docs/diagnostics/codes/m0019) | internal compiler error |
| [M0020](/docs/diagnostics/codes/m0020) | runtime panic |
| [M0021](/docs/diagnostics/codes/m0021) | fatal runtime error |
| [M0022](/docs/diagnostics/codes/m0022) | test failed |
| [M0023](/docs/diagnostics/codes/m0023) | import cycle |
| [M0024](/docs/diagnostics/codes/m0024) | name not exported |
| [M0025](/docs/diagnostics/codes/m0025) | not supported by marmot |
| [M0026](/docs/diagnostics/codes/m0026) | type argument does not satisfy constraint |
| [M0027](/docs/diagnostics/codes/m0027) | cannot infer type argument |
| [M0028](/docs/diagnostics/codes/m0028) | shadowed variable |
| [M0029](/docs/diagnostics/codes/m0029) | unknown struct field |
## Check findings and reports [#check-findings-and-reports]
| Code | What it means |
| --- | --- |
| [M0100](/docs/diagnostics/codes/m0100) | vet finding |
| [M0101](/docs/diagnostics/codes/m0101) | lint finding |
| [M0102](/docs/diagnostics/codes/m0102) | modernize suggestion |
| [M0103](/docs/diagnostics/codes/m0103) | not formatted |
| [M0104](/docs/diagnostics/codes/m0104) | gopls finding |
| [M0105](/docs/diagnostics/codes/m0105) | known vulnerability |
| [M0106](/docs/diagnostics/codes/m0106) | go.mod not tidy |
| [M0107](/docs/diagnostics/codes/m0107) | cross build failure |
| [M0108](/docs/diagnostics/codes/m0108) | unreachable function |
| [M0109](/docs/diagnostics/codes/m0109) | switch misses cases |
| [M0110](/docs/diagnostics/codes/m0110) | possible nil dereference |
| [M0111](/docs/diagnostics/codes/m0111) | heap allocation |
# M0000 error (/docs/diagnostics/codes/m0000)
The message says what is wrong.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0001 syntax error (/docs/diagnostics/codes/m0001)
The position is where the parser gave up, which is often just after the real mistake: look at the end of the previous line for a missing bracket, comma or operand.
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
func main() {
x := 1 +
}
```
```go title="Fixed"
func main() {
x := 1 + 2 // [!code highlight]
_ = x // [!code highlight]
}
```
## The fix [#the-fix]
Complete the expression or statement, or close the open bracket.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0002 undefined name (/docs/diagnostics/codes/m0002)
It may be misspelled, declared in a file excluded by build tags, or belong to a package that is not imported.
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
fmt.Printn("hi")
n := strings.Count(s, "a") // strings not imported
```
```go title="Fixed"
import "strings" // [!code highlight]
fmt.Println("hi") // [!code highlight]
n := strings.Count(s, "a") // [!code highlight]
```
## The fix [#the-fix]
Fix the spelling, or import the package that declares it.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0003 no such field or method (/docs/diagnostics/codes/m0003)
For methods, check the receiver: a method with a pointer receiver is not in the method set of the value type when the value is not addressable.
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
var b strings.Builder
b.WriteStrin("x")
```
```go title="Fixed"
var b strings.Builder
b.WriteString("x") // [!code highlight]
```
## The fix [#the-fix]
Use one of the fields or methods of the type.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0004 declared and not used (/docs/diagnostics/codes/m0004)
Assigning to a variable does not count as a use.
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
count := len(xs) // never read
```
```go title="Fixed"
// remove it, or use it: // [!code highlight]
count := len(xs) // [!code highlight]
fmt.Println(count) // [!code highlight]
```
## The fix [#the-fix]
Use the variable, remove it, or assign the value to _.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0005 imported and not used (/docs/diagnostics/codes/m0005)
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
import "os" // nothing uses os
```
```go title="Fixed"
// remove the import, or for side effects only: // [!code highlight]
import _ "net/http/pprof" // [!code highlight]
```
## The fix [#the-fix]
Remove the import, or use _ as its name if it is imported only for its init side effects.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0006 type mismatch (/docs/diagnostics/codes/m0006)
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
func add(a, b int) int
add(1, "two")
```
```go title="Fixed"
add(1, 2) // [!code highlight]
```
## The fix [#the-fix]
Convert the value, or change the declaration of the destination.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0007 mismatched types (/docs/diagnostics/codes/m0007)
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
var n int
var f float64
_ = n + f
```
```go title="Fixed"
_ = float64(n) + f // [!code highlight]
```
## The fix [#the-fix]
Convert one operand to the type of the other.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0008 wrong number of return values (/docs/diagnostics/codes/m0008)
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
func main() {
return 1
}
```
```go title="Fixed"
func f() int { // [!code highlight]
return 1
}
```
## The fix [#the-fix]
Return exactly the values in the signature, or change the signature.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0009 wrong number of arguments (/docs/diagnostics/codes/m0009)
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
func pair() (int, int)
pair(1)
```
```go title="Fixed"
pair() // [!code highlight]
```
## The fix [#the-fix]
Pass the arguments the signature lists.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0010 missing return (/docs/diagnostics/codes/m0010)
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
func sign(x int) int {
if x < 0 {
return -1
}
}
```
```go title="Fixed"
func sign(x int) int {
if x < 0 {
return -1
}
return 1 // [!code highlight]
}
```
## The fix [#the-fix]
Add a return at the end, or a panic for a case that cannot happen.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0011 package not found (/docs/diagnostics/codes/m0011)
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
import "example.com/lib" // not in go.mod
```
```go title="Fixed"
// go get example.com/lib, then // [!code highlight]
import "example.com/lib" // [!code highlight]
```
## The fix [#the-fix]
Run go get for the module, fix the import path, or remove the import.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0012 assignment count mismatch (/docs/diagnostics/codes/m0012)
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
n := strconv.Atoi(s)
```
```go title="Fixed"
n, err := strconv.Atoi(s) // [!code highlight]
if err != nil { // [!code highlight]
return err // [!code highlight]
} // [!code highlight]
```
## The fix [#the-fix]
Receive every result, using _ for the ones you do not need.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0013 no new variables (/docs/diagnostics/codes/m0013)
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
x := 1
x := 2
```
```go title="Fixed"
x := 1
x = 2 // [!code highlight]
```
## The fix [#the-fix]
Use = instead of :=.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0014 does not implement interface (/docs/diagnostics/codes/m0014)
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
type Shape interface{ Area() float64 }
type Square struct{ s float64 }
func (q *Square) Area() float64 { return q.s * q.s }
var s Shape = Square{2}
```
```go title="Fixed"
var s Shape = &Square{2} // *Square has Area // [!code highlight]
```
## The fix [#the-fix]
Add the missing method, fix its signature, or pass a pointer when the method has a pointer receiver.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0015 redeclared (/docs/diagnostics/codes/m0015)
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
func f() {}
func f() {}
```
```go title="Fixed"
func f() {}
func g() {} // [!code highlight]
```
## The fix [#the-fix]
Rename one of the declarations or remove the duplicate.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0016 invalid operation (/docs/diagnostics/codes/m0016)
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
var m, n map[string]int
_ = m < n // maps have no order
```
```go title="Fixed"
_ = len(m) < len(n) // [!code highlight]
```
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0017 bad command line (/docs/diagnostics/codes/m0017)
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0018 link error (/docs/diagnostics/codes/m0018)
## The fix [#the-fix]
If the function comes from the standard library, marmot does not support it yet; report it. For your own code, give the function a body.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0019 internal compiler error (/docs/diagnostics/codes/m0019)
This is a bug in marmot, not in your code. The report bundle has what is needed to reproduce it.
## The fix [#the-fix]
Report the bug with the bundle. As a workaround try -O 0, or MARMOT_TIER0=0.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0020 runtime panic (/docs/diagnostics/codes/m0020)
The trace lists the goroutine's frames, innermost first.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0021 fatal runtime error (/docs/diagnostics/codes/m0021)
Fatal errors cannot be recovered.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0022 test failed (/docs/diagnostics/codes/m0022)
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0023 import cycle (/docs/diagnostics/codes/m0023)
Go does not allow import cycles. Move the shared code into a third package that both import.
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
// package a imports b, package b imports a
```
```go title="Fixed"
// move what both need into package c; a and b import c // [!code highlight]
```
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0024 name not exported (/docs/diagnostics/codes/m0024)
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
i := strings.indexFunc(s, unicode.IsSpace)
```
```go title="Fixed"
i := strings.IndexFunc(s, unicode.IsSpace) // [!code highlight]
```
## The fix [#the-fix]
Use the exported name (upper case first letter).
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0025 not supported by marmot (/docs/diagnostics/codes/m0025)
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0026 type argument does not satisfy constraint (/docs/diagnostics/codes/m0026)
For a ~T term, the underlying type of the argument must be T.
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
type Number interface{ ~int | ~float64 }
func Sum[T Number](xs []T) T
Sum([]float32{1})
```
```go title="Fixed"
type Number interface{ ~int | ~float32 | ~float64 } // [!code highlight]
func Sum[T Number](xs []T) T
Sum([]float32{1})
```
## The fix [#the-fix]
Use a type in the set, or widen the constraint.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0027 cannot infer type argument (/docs/diagnostics/codes/m0027)
Give the type argument explicitly.
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
func Zero[T any]() T
x := Zero()
```
```go title="Fixed"
x := Zero[int]() // [!code highlight]
```
## The fix [#the-fix]
Write the type arguments in brackets after the function name.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0028 shadowed variable (/docs/diagnostics/codes/m0028)
Assignments go to the inner one, which is then unused, and the outer one keeps its old value. This often loses an error.
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
var err error
if ok {
data, err := load() // a new err
use(data)
}
return err // always nil
```
```go title="Fixed"
var err error
if ok {
var data []byte // [!code highlight]
data, err = load() // [!code highlight]
use(data)
}
return err // [!code highlight]
```
## The fix [#the-fix]
Declare the other variables first and assign with =.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0029 unknown struct field (/docs/diagnostics/codes/m0029)
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
u := User{Nmae: "ada"}
```
```go title="Fixed"
u := User{Name: "ada"} // [!code highlight]
```
## The fix [#the-fix]
Use one of the struct's fields.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0100 vet finding (/docs/diagnostics/codes/m0100)
The note names the analyzer.
## The fix [#the-fix]
Follow the finding; marmot fix applies the machine applicable ones.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0101 lint finding (/docs/diagnostics/codes/m0101)
The note names the analyzer (SA, S, ST and QF numbers follow staticcheck).
## The fix [#the-fix]
Follow the finding, or silence it for one line with a //lint:ignore comment that says why.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0102 modernize suggestion (/docs/diagnostics/codes/m0102)
Behavior is unchanged.
## The fix [#the-fix]
marmot fix applies these.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0103 not formatted (/docs/diagnostics/codes/m0103)
## The fix [#the-fix]
Run marmot fmt.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0104 gopls finding (/docs/diagnostics/codes/m0104)
## The fix [#the-fix]
Follow the finding; marmot fix applies the machine applicable ones.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0105 known vulnerability (/docs/diagnostics/codes/m0105)
Only vulnerabilities in reached code are reported.
## The fix [#the-fix]
Upgrade the module to the fixed version the finding names.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0106 go.mod not tidy (/docs/diagnostics/codes/m0106)
## The fix [#the-fix]
Run go mod tidy.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0107 cross build failure (/docs/diagnostics/codes/m0107)
## The fix [#the-fix]
Add the missing implementation for that platform, or a build tag that excludes the package there.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0108 unreachable function (/docs/diagnostics/codes/m0108)
## The fix [#the-fix]
Remove it, or use it.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0109 switch misses cases (/docs/diagnostics/codes/m0109)
A value added to the enum later then goes unnoticed.
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
type Color int
const (Red Color = iota; Green; Blue)
switch c {
case Red, Green:
}
```
```go title="Fixed"
switch c {
case Red, Green:
case Blue: // [!code highlight]
// handled, or nothing to do // [!code highlight]
}
```
## The fix [#the-fix]
Handle the missing values, add a default clause, or write //exhaustive:ignore on the line before the switch. marmot fix adds the missing cases with empty bodies, which keeps the behavior.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0110 possible nil dereference (/docs/diagnostics/codes/m0110)
A use dominated by a nil check is not reported.
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
func find(id int) (*User, error) {
if id == 0 {
return nil, nil
}
...
}
u, err := find(id)
if err != nil {
return err
}
fmt.Println(u.Name) // u is nil for id 0
```
```go title="Fixed"
u, err := find(id)
if err != nil {
return err
}
if u == nil { // [!code highlight]
return errNotFound // [!code highlight]
}
fmt.Println(u.Name) // [!code highlight]
```
## The fix [#the-fix]
Check for nil before the use, or make the function return an error instead of nil, nil.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# M0111 heap allocation (/docs/diagnostics/codes/m0111)
Heap allocations cost time in the allocator and the garbage collector; those in loops cost on every iteration.
## The mistake and the fix [#the-mistake-and-the-fix]
```go title="The mistake"
func newPoint() *Point {
return &Point{1, 2} // returned: heap
}
```
```go title="Fixed"
func point() Point { // [!code highlight]
return Point{1, 2} // a value: no allocation // [!code highlight]
}
```
## The fix [#the-fix]
Return values instead of pointers, pass buffers in, keep large arrays out of loops, or avoid interfaces on hot paths.
See also [all error codes](/docs/diagnostics/codes) and [marmot explain](/docs/commands/explain).
# Diagnostics (/docs/diagnostics)
Every error has a stable code (`M0002`) that `marmot explain` describes. The same diagnostic is
written in one of three modes:
| Mode | When | What it looks like |
| --- | --- | --- |
| human | stderr is a terminal | `error[CODE]: message`, then `--> file:line:col`, the source with carets under the exact span and a label saying what is wrong there, labels on related spans, `= note:` and `= help:` lines, and fixes as `help:` with a diff. At most 10 errors, sorted by file and line, then a summary. |
| plain | stderr is not a terminal | `file:line:col: message` lines, as the go tool prints them. Tools and tests parse this form; it does not change. |
| json | `-json`, `MARMOT_OUTPUT=json`, or a coding agent environment | JSON Lines on stderr. See [JSON output](/docs/diagnostics/json). |
`MARMOT_OUTPUT=human`, `plain` or `json` overrides the choice. `-color=auto|always|never` sets
color in human mode; `auto` respects `NO_COLOR` and `TERM=dumb`.
## Human [#human]
The parts, top to bottom:
1. The severity and code, then the message.
2. `-->` and the position of the primary span.
3. The source lines involved, with carets under the primary span and dashes under related
spans, each with a label. Lines in between are folded as `...`.
4. `= note:` lines with what the compiler knows, such as the method a type lacks.
5. `help:` with a suggested fix shown as a diff. `(check it)` marks fixes that are likely but
not certain.
6. A summary and the `marmot explain` command for the code.
## Plain [#plain]
The same error off a terminal, for scripts and editors that parse `go build` output:
```text
./main.go:12:16: cannot use Square{…} (value of struct type Square) as Shape value in variable declaration: Square does not implement Shape (method Area has pointer receiver)
```
## JSON for coding agents [#json-for-coding-agents]
JSON is the default when one of these variables is set (to anything but `0`) and
`MARMOT_OUTPUT` is not: `CLAUDECODE`, `AI_AGENT`, `CODEX_SANDBOX`, `GEMINI_CLI`,
`CURSOR_AGENT`, `OPENCODE`, `GOOSE_TERMINAL`. Each diagnostic carries its spans, the enclosing
function, the source lines around it, the fixes as edits, and `rendered`, the exact human form.
An agent can apply a fix without opening the file. `marmot mcp` serves the same data over the
Model Context Protocol.
See [JSON output](/docs/diagnostics/json) for the schema, and [Error codes](/docs/diagnostics/codes)
for every code.
## Runtime errors [#runtime-errors]
A program built by marmot reports a panic or fatal error in the same three modes, chosen when
it happens.
- On a terminal the report lists the program's frames with their source lines and folds
library frames.
- Off a terminal the trace is the plain Go form.
- With `MARMOT_OUTPUT=json` it writes one object:
```json
{"schema":1, "kind":"panic", "code":"M0020", "message", "valueType", "chain", "goroutine",
"frames":[{"func", "file", "line", "col", "inlined", "user"}],
"goroutines":{"total", "running", "runnable", "waiting"}, "exitCode":2}
```
`kind` is `fatal` and the code `M0021` for fatal errors. `marmot run` in JSON mode passes
`MARMOT_OUTPUT=json` to the program.
# JSON output (/docs/diagnostics/json)
Each line is one object with `"schema": 1` and a `kind`. A command writes zero or more
`diagnostic` lines and ends with one `result` line.
```json
{"schema":1,"kind":"diagnostic","severity":"error","code":"M0002","message":"undefined: fmt.Printn",
"span":{"file":"/src/app/main.go","line":15,"col":6,"endLine":15,"endCol":12},
"help":["a func with a similar name exists: fmt.Println"],
"fixes":[{"title":"use fmt.Println","edits":[{"file":"/src/app/main.go",
"span":{"file":"/src/app/main.go","line":15,"col":6,"endLine":15,"endCol":12},"newText":"Println"}]}],
"context":{"function":"func main()","lines":[{"line":15,"text":"\tfmt.Printn(\"x\")"}]},
"explain":"marmot explain M0002"}
{"schema":1,"kind":"result","command":"build","ok":false,"errors":1,"warnings":0,"durationMs":310,"exitCode":1}
```
New fields may be added within schema 1; parsers must ignore fields they do not know.
## diagnostic [#diagnostic]
| Field | Meaning |
| --- | --- |
| `severity` | `error`, `warning` or `note`. |
| `label` | What is wrong at the primary span, readable on its own. |
| `code` | Stable code, `M0000` when there is no more specific one. See [Error codes](/docs/diagnostics/codes). |
| `message` | The message, as in plain mode. |
| `span` | Primary location. Lines and columns are 1-based, columns count bytes, `endCol` is exclusive. Absent location: `file` is empty. |
| `labels` | Secondary spans, each `{span, text}`. |
| `notes`, `help` | Lists of strings. |
| `fixes` | Suggested fixes, each `{title, applicability, edits}`; an edit is `{file, span, newText}` and replaces the span (an empty span inserts). `applicability` is `machine-applicable` (safe to apply unseen), `maybe-incorrect` (likely, check it) or `has-placeholders` (never apply as is). Applying every edit of one fix resolves the diagnostic. |
| `related` | Other locations, each `{span, message}`, for example the declaration of a called function. |
| `context` | What you would open the file for: `function` (enclosing signature), `lines` (source around the span, `{line, text}`), `types` (one line definitions of the types the message names). |
| `bundle` | Internal compiler errors only: a directory with the inputs for a bug report. |
| `rendered` | The human form without color, exactly what a person would see. |
| `fullMessage` | Present when `message` was shortened (long type names elided). |
| `explain` | The command that describes the code. |
In JSON mode all errors are listed (no cap), duplicates removed, in a stable order.
## result [#result]
`command`, `ok`, `errors`, `warnings`, `output` (the file written, on success), `durationMs`,
`exitCode`.
## Other commands [#other-commands]
Other commands use the same envelope (`schema`, `kind`) with their own kinds:
| Command | Kinds |
| --- | --- |
| `marmot test -json` | `test` per test, `diagnostic` per failure, `result` |
| `marmot fix -json` | `fix` per applied fix (`file`, `code`, `title`, `line`), then `diagnostic` and `result` |
| `marmot fmt -json` | `format` per changed file (`file`, `written`), `result` |
| `marmot generate -json` | `generate` per directive (`command`, `cached`, `outputs`), `result` |
| `marmot watch -json` | `watch` events (`build`, `done`) around each round's own output |
| `marmot size -json` | one `size` object: `sections`, `packages`, `functions`; `-why` gives `why` with `chain` |
| `marmot bench -json` | `bench` per benchmark and unit, with the summaries, `delta` and `p` |
| `marmot prof -json` | one `profile` object: `functions`, `lines`, and `tree` with `-tree` |
| `marmot doctor -json` | `doctor` per check (`check`, `status`, `detail`, `fix`), `result` |
| `marmot check -json` | `diagnostic` per finding with `check` and `analyzer`, `check` per check, `result` |
| `marmot explain -json CODE` | `{code, title, text, wrong, right, fix}` on stdout |
# FAQ (/docs/faq)
## Is marmot a fork of Go? [#is-marmot-a-fork-of-go]
No. marmot uses Go's own type checker, SSA builder and standard library source, so it compiles
the same language with the same library. It replaces the code generator, the linker step and the
runtime: code goes through MLIR and LLVM, and the runtime is marmot's own.
## Do I need to change my code? [#do-i-need-to-change-my-code]
No. marmot builds ordinary Go modules with `go.mod`, build tags, tests and `//go:generate`. The
[known differences](/docs/support#known-differences-from-go) are cases the Go specification
leaves open.
## Is it ready for production? [#is-it-ready-for-production]
Not yet. It compiles real programs, including itself, and checks every test program's output
against Go, but it is young. Use it where a wrong result is cheap to notice, and keep `go build`
for what you ship until you have checked it on your code.
## Why are the binaries so small? [#why-are-the-binaries-so-small]
The runtime is small and starts little up front, and release builds optimize the whole program
together, so code that nothing calls is removed. `-O z` and `-s` make them smaller still.
`marmot size -why FUNC` says why a function is in a binary.
## Why are some programs slower than with Go? [#why-are-some-programs-slower-than-with-go]
Mostly allocation heavy code and `defer` in hot loops, which Go has tuned for years. See
[Benchmarks](/docs/benchmarks#by-kind-of-program).
## Can I keep using go test, go vet and gopls? [#can-i-keep-using-go-test-go-vet-and-gopls]
Yes. marmot does not change your module, so the go tools keep working next to it. `marmot test`
and `marmot check` are there when you want one tool that does both.
## Does it work with coding agents? [#does-it-work-with-coding-agents]
Yes. Under a coding agent marmot writes its diagnostics as JSON, with spans, source context and
fixes as edits, and `marmot mcp` serves build, run, test, fix and explain over the Model Context
Protocol. See [Diagnostics](/docs/diagnostics#json-for-coding-agents).
## Does it support cgo? [#does-it-support-cgo]
Yes, as of recently: packages that import `"C"` build, and C calls are direct native calls. Cross
builds of cgo packages do not work yet. See [cgo](/docs/cgo).
## Which platforms? [#which-platforms]
macOS on Apple Silicon and Linux on x86-64. Linux on arm64 builds; Windows is not supported.
## Why the name? [#why-the-name]
A marmot is a close relative of the gopher, and it starts with M, like MLIR.
# Getting started (/docs/getting-started)
This page assumes marmot is installed and `marmot doctor` reports no problems. If not, see
[Install](/docs/install).
## Build and run [#build-and-run]
marmot works on any Go module. Make one:
```sh
mkdir hello && cd hello
go mod init hello
```
```go title="main.go"
package main
import "fmt"
func main() {
name := "marmot"
fmt.Println("hello,", name)
}
```
Run it, then build an executable. The commands are the go command's, with `marmot` in front:
```sh
marmot run main.go
marmot build -o hello .
./hello
```
```sh
go run main.go
go build -o hello .
./hello
```
The first build compiles the standard library packages the program uses and caches them, so
later builds are fast. For the binary you ship, use release mode:
```sh
marmot build -release -o hello .
```
## Read an error [#read-an-error]
Change `Println` to `Printn` and build again. marmot shows the source, marks the exact span,
says what is wrong there, and suggests a fix:
The fix is marked `check it` because it is a guess based on the name. `marmot fix -all` applies
it; plain `marmot fix` applies only fixes that are always safe, such as removing an unused
import. `marmot explain M0002` describes the error with an example.
Errors that involve more than one place point at each of them:
## Test [#test]
`marmot test` runs package tests like `go test`. On a terminal each failure comes first, at the
line of its `t.Error` or `t.Fatal`:
## Next [#next]
- [Build modes](/docs/build-modes): dev and release, and what each is for.
- [Commands](/docs/commands): every command, with its flags.
- [Diagnostics](/docs/diagnostics): human, plain and JSON output, and every error code.
# Introduction (/docs)
marmot compiles Go programs to native executables through MLIR and LLVM. It works like the
`go` command: run it inside a Go module, with the same packages, flags and tests.
```sh
marmot build ./cmd/app # fast rebuilds
marmot build -release ./cmd/app # whole program optimization
marmot run main.go
marmot test ./...
```
marmot compiles real Go programs, including itself, but it is new and not ready for
production use. See [Support](/docs/support) for what works and what does not.
## Why marmot [#why-marmot]
- **Small binaries.** A hello world with `println` is 148 KB in release mode, against
1.68 MB from `go build`. With `fmt.Println` it is 665 KB against 2.4 MB.
- **Fast programs.** Release builds run 1.6x faster than Go on average over 43
programs, with every program's output checked against Go.
- **Fast builds.** A rebuild with nothing changed takes 0.05 to 0.07 s on a 238
package program, against 0.14 to 0.15 s.
- **Errors that teach.** Every error shows the source, the exact span, a label that says what
is wrong there, and a suggested fix that `marmot fix` can apply. Coding agents get the same
information as JSON.
## The tools you install next to go [#the-tools-you-install-next-to-go]
marmot also does what people usually install separate tools for: `marmot fmt` (goimports),
`marmot check` (vet, golangci-lint, govulncheck, tidy and more, in one pass), `marmot fix`,
`marmot watch`, `marmot repl`, `marmot bench` (benchstat built in), `marmot prof` (pprof in the
terminal), `marmot size`, `marmot generate` with a cache, `marmot doctor`, and `marmot mcp` for
coding agents. See [Commands](/docs/commands).
## How it works [#how-it-works]
marmot keeps the parts of the Go toolchain that define the language (the `go/types` checker,
the SSA builder, Go's own standard library source) and replaces the parts that generate code.
[Design](/docs/design) explains each step.
## The name [#the-name]
A marmot is a close relative of the gopher, and it starts with M, like MLIR. Marmots stand
guard on a rock and whistle when something is wrong, which is what marmot's error messages are
for.
# Install (/docs/install)
marmot is built from source for now. There are no release binaries yet.
## Requirements [#requirements]
| Tool | Version |
| --- | --- |
| Go | 1.27 |
| LLVM and MLIR | 22 (clang, lld and the MLIR libraries) |
| CMake and Ninja | recent |
marmot runs on macOS on Apple Silicon and on Linux on x86-64. Linux on arm64 builds but is not
yet tested; Windows is not supported. See [Support](/docs/support).
## Build and install [#build-and-install]
```sh
git clone https://github.com/Gaurav-Gosain/marmot
cd marmot
make # the front end, the backend and the runtime
make test # optional: the test suites
make install # copies the compiler to ~/.marmot and links ~/.local/bin/marmot
```
`make install` copies the build, so rebuilding the source tree later does not change the
installed compiler. `PREFIX` and `BINDIR` choose other places:
```sh
make install PREFIX=/opt/marmot BINDIR=/usr/local/bin
```
## Check the installation [#check-the-installation]
```sh
marmot doctor
```
`marmot doctor` checks the code generator and runtime, the `go` command, LLVM, the system
linker, the build cache and free disk, and says how output is chosen in this terminal. Every
failed check comes with what to do about it.
## Shell completion [#shell-completion]
```sh
marmot completion zsh > "${fpath[1]}/_marmot"
marmot completion bash > /etc/bash_completion.d/marmot
marmot completion fish > ~/.config/fish/completions/marmot.fish
```
# Support matrix (/docs/support-matrix)
Generated from marmot's test results of 2026-11-12. Every test builds a program with marmot and with Go and compares the output and exit status.
Status words: **works** (every test passes), **partial** (some fail), **missing** (not implemented), **untested**.
## Conformance numbers [#conformance-numbers]
Every test compares stdout, stderr (up to the first panic line) and exit status with the stock Go toolchain.
| Tier | Passing | Total | What it covers |
| --- | --- | --- | --- |
| run/core | 134 | 134 | language core, no imports, `println` only |
| run/gc | 13 | 13 | allocation heavy programs |
| run/sched | 15 | 15 | goroutines, channels, deadlock |
| run/std | 235 | 238 | standard library by package |
| run/opt | 21 | 22 | optimizer and specializer targets |
| run/mem | 50 | 50 | use after free and lifetime hunting (every way a slice stays reachable) |
| run/layout | 49 | 49 | struct layouts: every field mix, every access path |
| run/audit | 33 | 41 | counterexamples from the spec and verify audits |
| run/apps | 12 | 12 | 200 to 600 line real programs |
| goroot | 98 | 108 | single file programs from the Go project's own test suite |
| world | 29 | 32 | real open source modules (vendored, pinned) |
| **total (dev build)** | **689** | **714** | |
Other build modes in the same run:
| Mode | Passing | Total |
| --- | --- | --- |
| dev | 689 | 714 |
| gcstress | 50 | 50 |
| lifetime | 49 | 50 |
## Language [#language]
| Feature | Status | Tests |
| --- | --- | --- |
| Integers: wraparound, shifts, division, conversions | works | 13 of 13 |
| Floats: NaN, infinities, conversions, printing | works | 6 of 6 Fused multiply-add follows gc on arm64. |
| Complex numbers | works | 2 of 2 |
| Strings, runes, UTF-8 iteration and conversion | works | 7 of 7 |
| Slices: append, copy, aliasing, capacity | works | 10 of 10 Growth capacities may differ from gc. |
| Arrays and structs: copy, compare, embedding | works | 9 of 9 |
| Maps, all key kinds, iteration with deletion | works | 8 of 8 Iteration order is random, as in gc. |
| Methods, method values and expressions | works | 2 of 2 |
| Closures and Go 1.22 loop variables | works | 4 of 4 |
| Interfaces, assertions, type switches | works | 12 of 12 |
| Generics: functions, types, constraints | works | 5 of 5 |
| defer, panic, recover | works | 18 of 18 |
| Runtime panics: bounds, nil, divide, assertions | works | 8 of 8 Message text matches gc; the stack trace format differs. |
| Control flow: labels, goto, switch, range over int and func | works | 10 of 10 |
| Constants and iota | works | 2 of 2 |
| Initialization order and init functions | works | 2 of 2 |
| Evaluation order | works | 2 of 2 Variables are read after calls in the same expression, as gc does. |
| Goroutines, channels, select | works | 26 of 26 |
| Garbage collection (allocation heavy programs) | works | 13 of 13 |
| unsafe | works | 5 of 5 |
| Compiler optimizations preserve semantics | partial | 21 of 22 Loop, specialization and float tests; run at -O0, -O2, -Oz and -release. |
| Real programs (parsers, interpreters, simulations) | works | 12 of 12 |
| cgo | missing | non-goal for v1 |
| Race detector (-race) | missing | non-goal for v1 |
| plugin package | missing | non-goal for v1 |
| DWARF debug info | missing | non-goal for v1 |
| Windows | missing | non-goal for v1 |
## Standard library [#standard-library]
Each row is a directory under `tests/run/std`. Packages not listed have no test yet.
| Packages | Status | Tests |
| --- | --- | --- |
| sync/atomic | works | 10 of 10 |
| bufio | works | 11 of 11 |
| bytes | works | 10 of 10 |
| compress/* | works | 1 of 1 |
| encoding/{base32,base64,hex,binary,csv,xml,gob,asn1,pem,ascii85} | works | 4 of 4 |
| errors | works | 10 of 10 |
| path, path/filepath | works | 5 of 5 |
| fmt | works | 14 of 14 |
| container/{heap,list,ring} | works | 3 of 3 |
| net/http, httptest, cookiejar, crypto/tls | works | 8 of 8 |
| io | works | 11 of 11 |
| encoding/json | works | 8 of 8 |
| log | partial | 1 of 2 |
| maps | works | 10 of 10 |
| math, math/bits, math/cmplx | works | 12 of 12 |
| net, net/netip | works | 6 of 6 |
| os, os/user | works | 12 of 12 |
| math/rand | works | 3 of 3 |
| reflect | partial | 7 of 8 |
| regexp | works | 6 of 6 |
| runtime, runtime/debug | missing | 0 of 1 |
| slices, cmp | works | 10 of 10 |
| log/slog | works | 1 of 1 |
| sort | works | 10 of 10 |
| database/sql | works | 1 of 1 |
| strconv | works | 10 of 10 |
| strings | works | 10 of 10 |
| sync | works | 12 of 12 |
| text/template | works | 5 of 5 |
| time, time/tzdata | works | 14 of 14 |
| unicode, unicode/utf8, unicode/utf16 | works | 10 of 10 |
### Stock standard library tests under `marmot test` [#stock-standard-library-tests-under-marmot-test]
Results of `marmot test -short` on stock packages (darwin/arm64 laptop, 2026-11-12, before the fixes). "alloc counts only" means every failure is a `testing.AllocsPerRun` count.
| package | result | pass | fail | rate |
| --- | --- | --- | --- | --- |
| fmt | ok | 79 | 0 | 100% |
| strings | FAIL | 146 | 5 | 97% |
| bytes | FAIL | 175 | 3 | 98% |
| strconv | FAIL | 69 | 11 | 86% |
| sort | ok | 81 | 0 | 100% |
| slices | FAIL | 162 | 3 | 98% |
| maps | ok | 24 | 0 | 100% |
| math | ok | 111 | 0 | 100% |
| unicode | ok | 42 | 0 | 100% |
| unicode/utf8 | FAIL | 30 | 1 | 97% |
| time | FAIL | 46 | 2 | 96% |
| sync | build | 0 | 0 | - |
| sync/atomic | ok | 107 | 0 | 100% |
| context | FAIL | 68 | 1 | 99% |
| errors | ok | 92 | 0 | 100% |
| io | FAIL | 77 | 2 | 97% |
| bufio | FAIL | 88 | 1 | 99% |
| os | FAIL | 3525 | 11 | 100% |
| os/exec | FAIL | 23 | 57 | 29% |
| os/signal | FAIL | 5 | 1 | 83% |
| path | ok | 16 | 0 | 100% |
| path/filepath | FAIL | 59 | 1 | 98% |
| encoding/json | FAIL | 0 | 1 | 0% |
| encoding/binary | build | 0 | 0 | - |
| encoding/base64 | ok | 23 | 0 | 100% |
| encoding/hex | ok | 18 | 0 | 100% |
| encoding/csv | ok | 77 | 0 | 100% |
| encoding/xml | FAIL | 332 | 3 | 99% |
| encoding/pem | ok | 21 | 0 | 100% |
| regexp | ok | 75 | 0 | 100% |
| text/template | FAIL | 58 | 1 | 98% |
| html/template | FAIL | 0 | 0 | - |
| text/tabwriter | ok | 6 | 0 | 100% |
| text/scanner | ok | 22 | 0 | 100% |
| net/url | FAIL | 89 | 5 | 95% |
| net/http | build | 0 | 0 | - |
| net/http/httptest | FAIL | 51 | 3 | 94% |
| net | FAIL | 9 | 0 | 100% |
| mime | FAIL | 24 | 1 | 96% |
| mime/multipart | FAIL | 49 | 4 | 92% |
| crypto/sha256 | FAIL | 33 | 2 | 94% |
| crypto/md5 | FAIL | 17 | 1 | 94% |
| crypto/sha1 | FAIL | 17 | 1 | 94% |
| crypto/hmac | ok | 233 | 0 | 100% |
| crypto/rand | FAIL | 14 | 2 | 88% |
| crypto/aes | ok | 58 | 0 | 100% |
| crypto/tls | FAIL | 0 | 0 | - |
| crypto/x509 | FAIL | 0 | 0 | - |
| hash/crc32 | ok | 18 | 0 | 100% |
| hash/fnv | ok | 62 | 0 | 100% |
| container/heap | ok | 9 | 0 | 100% |
| container/list | ok | 11 | 0 | 100% |
| container/ring | ok | 15 | 0 | 100% |
| math/rand | FAIL | 25 | 2 | 93% |
| math/rand/v2 | ok | 25 | 0 | 100% |
| math/big | FAIL | 266 | 1 | 100% |
| math/bits | build | 0 | 0 | - |
| reflect | build | 0 | 0 | - |
| runtime | build | 0 | 0 | - |
| log | FAIL | 9 | 2 | 82% |
| log/slog | FAIL | 225 | 29 | 89% |
| flag | FAIL | 28 | 1 | 97% |
| embed | build | 0 | 0 | - |
| io/fs | FAIL | 5 | 0 | 100% |
| compress/gzip | ok | 23 | 0 | 100% |
| compress/flate | ok | 128 | 0 | 100% |
| archive/tar | FAIL | 1 | 2 | 33% |
| archive/zip | FAIL | 0 | 0 | - |
| html | ok | 6 | 0 | 100% |
| go/token | FAIL | 25 | 1 | 96% |
| go/scanner | ok | 25 | 0 | 100% |
| go/parser | FAIL | 42 | 0 | 100% |
| go/ast | FAIL | 23 | 1 | 96% |
| go/format | ok | 5 | 0 | 100% |
| go/types | FAIL | 70 | 1 | 99% |
| iter | FAIL | 10 | 10 | 50% |
| unique | build | 0 | 0 | - |
## Real world modules [#real-world-modules]
Vendored at a pinned version under `tests/world` (see `tests/world/README.md`), each with a small deterministic program checked against stock Go.
| Module test | Status |
| --- | --- |
| base58 | works |
| blackfriday | works |
| cobra | works |
| decimal | works |
| difflib | works |
| doublestar | works |
| embedfs | works |
| fuzzysearch | works |
| gjson | works |
| gocmp | works |
| godiff | works |
| goldmark | works |
| gotoml | works |
| govaluate | works |
| humanize | works |
| ini | works |
| levenshtein | works |
| lru | works |
| mapstructure | fails |
| pflag | works |
| pkgerrors | works |
| rate | works |
| semver | works |
| strcase | works |
| tablewriter | works |
| testify | fails |
| toml | works |
| ulid | fails |
| uuid | works |
| xtext | works |
| xxhash | works |
| yaml | works |
29 of 32 world programs pass.
## Build modes [#build-modes]
- **dev** (default, `-O2`): the safe pipeline; every transform below that is marked off stays off.
- **`-release`**: whole program optimization, `-O3`, loop and library specializations.
- **`-O0` `-O1` `-O2` `-O3` `-Os` `-Oz`**: LLVM level; the conformance runner can run any program in `-O0`, `-O2`, `-Oz` and `-release` (`-modes`).
- **Opt-in lifetime and memory transforms**, each behind an environment switch. A transform is on by default only after a proof review and a full conformance matrix plus fuzz round with it forced on; until then it is off.
| Transform | Switch | Default |
| --- | --- | --- |
| In-place reuse (reuse.go) | MARMOT_REUSE | off |
| Capture by value (escape.go allocSettled) | MARMOT_BYVALUE | off |
| Region inference (region.go) | MARMOT_REGIONS | off |
| Frame regions (frameregion.go) | MARMOT_FRAME_REGIONS | off |
| Allocation sinking (allocsink.go) | MARMOT_ALLOCSINK | off |
| Escape summaries, cross package, extended rules | MARMOT_ESCAPE_SUMMARY, MARMOT_XPKG_ESCAPE, MARMOT_ESCAPE_EXT | off |
| Conversion and make frame buffers (stackconv.go) | MARMOT_STACKCONV | off |
| Intraprocedural escape (default path) | on | on |
| go-strings S3 no-copy (backend) | -no-strings-nocopy | on |
| Defer record elision (panics.go) | MARMOT_DEFERELIDE | off |
| Dead initializer removal (staticinit.go) | MARMOT_DEADINIT | off |
| Allocation without zeroing (rtcore rt_alloc_nozero, 569d095; poison fill c8e0a15) | callers only | on |
Stress modes used by the conformance runner: `gcstress` (`MARMOT_GCSTRESS=1`), `poison` (`MARMOT_POISON=1`), `lifetime` (`MARMOT_LIFETIME=all`).
## Targets [#targets]
| Target | Status | Tested |
| --- | --- | --- |
| darwin/arm64 | primary | all tiers above |
| linux/amd64 | cross compiled on macOS, run on a Linux test host | 99 of 99 conformance tests pass (tree `2c645f1`) |
| linux/arm64 | not tested | |
## Tools [#tools]
- `marmot build [flags] [packages or files]`: `-o`, `-O {0,1,2,3,s,z}` (default 2), `-release` (whole program, `-O3`, loop and library specializations), `-emit {mlir,llvm,obj,exe}`, `-dialect {llvm,go}`, `-gc {precise,none}`, `-target`, `-tags`, `-nocache`, `-v`, `-x`, `-work`.
- `marmot run [flags] file.go [args]`: build and run.
- `marmot test [-v] [-run RE] [-skip RE] [-bench RE] [-count N] [-timeout D] [packages]`: builds the `go list -test` main and runs it in the package directory.
- `marmot version`.
## Known differences from gc [#known-differences-from-gc]
- Panic output prints the same message and exits with status 2; the traceback below `goroutine N [running]:` is gc style but frames of inlined functions do not appear.
- Floating point uses the same fused multiply-add rule as gc on arm64 (`x*y+z` fuses, an explicit conversion does not).
- Variables are read after calls in the same expression, as gc does (the spec leaves this unspecified).
- `runtime.Caller` and friends work from a pc table; binaries built with `-s` have none.
- **NaN payloads.** Where the Go specification only says the result is a NaN, marmot may return a different NaN (payload and sign bits) than gc: the builtin `min` and `max` (also over slices, so `slices.Min` and `slices.Max`), `math.Max`, `math.Min`, and `math.Floor`, `Ceil`, `Trunc`, `Round` and `RoundToEven` of a signaling NaN (gc quiets it, marmot may return it unchanged). The value is a NaN in every case; only `math.Float64bits`, `math.Float32bits` or `unsafe` shows the difference, and gc itself differs here across architectures. Values that are not NaN are bit identical to gc on the same architecture; a difference there is a bug. Programs: `tests/run/audit/minmax_nan_payload`, `tests/run/audit/math_exp_log_bits`.
- Slice growth capacities and map iteration order are not guaranteed to equal gc's.
- Evaluation order: variables are read after the calls of the same expression; open cases are tracked in `tests/run/audit/evalorder_gaps`.
- Unexported methods of embedded types from different packages with the same name: see `world/audit_unexported` under open failures.
- cgo, the race detector, plugins, DWARF and Windows are not supported.
- `reflect`: see the open failures below for the current gaps.
## Open failures [#open-failures]
Listed in `tests/known_failures.txt`; a test that passes is removed from the list.
- `goroot/genmeth1`:
- `goroot/range4`:
- `goroot/reflectmethod1`:
- `goroot/reflectmethod2`:
- `goroot/reflectmethod3`:
- `goroot/reflectmethod4`:
- `goroot/reflectmethod5`:
- `goroot/reflectmethod6`:
- `goroot/reflectmethod7`:
- `goroot/shift3`:
- `run/std/reflect/kinds_names`:
- `run/opt/fma_fusion_gc`: gc fuses x*y+z on arm64, marmot does not (spec allows both)
- `run/opt/panic_in_loop@Oz`: recovered panic then nil deref panic is not recoverable at -Oz
- `run/std/runtime/goroutines_gc`: runtime.Caller unsupported (no pc to file:line table yet)
- `run/std/log/basic`: runtime.Caller unsupported, log.Lshortfile prints ???:0
- `world/ulid`: return id, parse(&id): gc reads variables after calls (see core/eval_order_var_after_call)
- `run/mem/regionstore@lifetime`: MARMOT_LIFETIME=all: region store use-after-free, expected until fixed
- `world/audit_unexported`: unexported methods of different packages are confused in itabs
- `run/audit/deadinit_bigmake`: huge make: fatal "out of memory" instead of the makeslice panic (dead initializer removal)
- `run/audit/defer_builtin_recover`: recover() result lost for a deferred builtin
- `run/audit/evalorder_gaps`: map index evaluation order differs from gc
- `run/audit/fma_complex_mul`: complex multiply: fused multiply-add bits differ from gc
- `run/audit/make_huge_const`: make with a huge constant length does not panic like gc
- `run/audit/math_exp_log_bits`: math.Exp(-745.13) returns 0x1, gc 0x0 (value difference, not a NaN payload)
- `run/audit/method_value_nil_iface`: method value of a nil interface panics with the wrong message
- `run/audit/tempstring_write`: temporary string written through (no-copy conversion)
# Support (/docs/support)
## Platforms [#platforms]
| Platform | State |
| --- | --- |
| macOS on Apple Silicon | Main development platform. |
| Linux on x86-64 | Works, tested with the same suites. |
| Linux on arm64 | Builds. Not yet run under test. |
| Windows | Not supported. |
## Language [#language]
The whole language is supported, including generics, closures, `defer`, `panic` and `recover`,
goroutines, channels, `select`, range over functions, and `unsafe`.
Packages that import `"C"` build; see [cgo](/docs/cgo). Not supported: plugins and the race
detector.
## Standard library [#standard-library]
marmot compiles Go's own standard library source. Most packages work. Packages checked by the
test suite include:
`bufio`, `bytes`, `context`, `crypto/sha256`, `encoding/base64`, `encoding/hex`,
`encoding/json`, `errors`, `flag`, `fmt`, `io`, `log`, `log/slog`, `maps`, `math`, `math/big`,
`net`, `net/http`, `os`, `os/exec`, `path/filepath`, `reflect` (most of it), `regexp`, `slices`,
`sort`, `strconv`, `strings`, `sync`, `sync/atomic`, `testing`, `text/template`, `time`,
`unicode/utf8`.
Known gaps: `reflect.MakeFunc`, and parts of `runtime` that expose Go's own scheduler
internals.
## Test results [#test-results]
Every test builds a program with both compilers and compares output and exit status.
| Suite | Passing | Total |
| --- | --- | --- |
| Core language | 134 | 135 |
| Garbage collection | 13 | 13 |
| Goroutines and channels | 15 | 15 |
| Standard library | 249 | 249 |
| Memory safety | 50 | 50 |
| Struct layout | 49 | 49 |
| Small applications | 12 | 12 |
| Programs from Go's own test suite | 106 | 108 |
| Real open source modules | 34 | 34 |
| All suites | 729 | 735 |
The full list, feature by feature and package by package, is in the
[support matrix](/docs/support-matrix).
## Known differences from Go [#known-differences-from-go]
These are cases where the Go specification allows more than one behavior and marmot does not
yet match what the standard Go compiler does.
| Area | Difference |
| --- | --- |
| Evaluation order | In a few statement shapes, a variable is read before a function call in the same statement, where Go reads it after. |
| NaN values | The exact bit pattern of a NaN result can differ. It is still a NaN. |
| Floating point | `math.Log` can differ in the last bit for some inputs on arm64. |
| Stack traces | Panic messages match Go. The stack trace that follows has a different layout. |