# gasm-devkit Developer tooling for **GAsm**, Go's built-in Plan 9 assembler. Go ships an assembler but no tooling for it: there is no syntax highlighting, no autocomplete, no linter, no static analyser, no formatter, no standalone assembler and no debugger for `.s` files. Developers write assembly blind, validate it by benchmark, and debug it by print statement. gasm-devkit is the missing toolkit: a single, self-contained binary, `gasm`, that brings proper developer tooling to Plan 9 assembly on amd64, arm64, riscv64 and loong64. ## Features - **Front end.** A hand-written lexer and an error-tolerant parser produce a typed AST with source positions; `gasm tokens` and `gasm parse` expose them directly. - **Formatter.** `gasm fmt` canonicalises indentation, operand spacing, per-function mnemonic alignment and blank-line layout: `gofmt` for assembly, operating recursively on directories the way `go fmt` does. - **Linter.** `gasm lint` runs 18 conservative static checks, among them `undefined-label`, `abi-argsize` (declared frame vs the `// func` signature), `register-clobber` (Go ABI register liveness over the control-flow graph), `stack-imbalance`, `abi0-register-args` and `unencodable-instruction`. - **Standalone assembler.** `gasm asm` encodes all four architectures without the Go toolchain and writes raw images, linkable ELF objects (with DWARF5 debug sections) or the Go toolchain's own GOOBJ format, which `go build` consumes in place of the toolchain's output. - **Dynamic verification.** `gasm verify` JIT-loads assembled functions into executable memory: smoke calls, ABI checks (sentinel registers, red-zone canary), differential fuzzing against the `go tool asm` build, and byte-for-byte ground-truth comparison of the machine code. - **Debugger.** `gasm debug` is a source-level ptrace debugger with breakpoints (optionally conditional), hardware watchpoints, register and memory inspection, and headless script runs with label-level coverage. - **Language server.** `gasm lsp` serves completion, hover, document symbols, push and pull diagnostics, semantic-token highlighting, go-to-definition, find references, rename, formatting, inlay hints, code actions, signature help, document highlights, workspace symbol search, #include document links and folding ranges over stdio. - **Comparators and audits.** `gasm diff` compares the machine code of two assembly files byte-for-byte, `gasm profile` shows basic-block structure, `gasm audit-instructions` diffs the encoder against the installed toolchain, and `gasm scaffold` generates a differential test skeleton for a kernel. - **Complete instruction coverage.** The instruction tables are generated from the Go toolchain's own assembler source, so the toolkit recognises every mnemonic the real assembler accepts; `just gen` refreshes them. ### Architecture support | Architecture | GOARCH | File suffix | Instructions recognised | |--------------|-------------|--------------|---------------------------------------------| | AMD64 | `amd64` | `_amd64.s` | 1600 + common opcodes + traditional aliases | | ARM64 | `arm64` | `_arm64.s` | 538 + common opcodes | | RISC-V | `riscv64` | `_riscv64.s` | 961 + common opcodes | | LoongArch | `loong64` | `_loong64.s` | 799 + common opcodes | "Common opcodes" are the instructions shared by every architecture (`RET`, `JMP`, `NOP`, `CALL`, `TEXT`, `FUNCDATA`, `PCDATA`, ...). AMD64 additionally carries the traditional conditional-jump spellings (`JZ`, `JNZ`, `JA`, `JC`, ...) that the assembler accepts as aliases. Regenerating the tables is one command (`just gen`) and requires only a Go installation; the committed output has no runtime dependency on the toolchain. ## Install Prebuilt binaries for linux/amd64, linux/arm64, linux/riscv64 and linux/loong64 are on the [releases page](https://sourcedock.dev/petrbalvin/gasm-devkit/releases). From source (Go 1.27 or later): ```sh go install sourcedock.dev/petrbalvin/gasm-devkit/cmd/gasm@latest ``` Or from a repository checkout, with the development version stamped: ```sh just install-bin ``` ## Quick start ```sh cat > hello_amd64.s <<'EOF' #include "textflag.h" // func add(a, b int) int TEXT ·add(SB), NOSPLIT, $0-24 MOVQ a+0(FP), AX ADDQ b+8(FP), AX MOVQ AX, ret+16(FP) RET EOF gasm lint hello_amd64.s # static checks gasm asm -o hello.bin hello_amd64.s # assemble to a raw image gasm verify --call add --args a=2,b=3 hello_amd64.s # JIT-call it with arguments ``` ## Usage ```sh gasm fmt # reformat every .s below here, like go fmt gasm fmt -w kernel_amd64.s # canonicalise one file in place gasm lint *.s # static checks gasm asm --format elf -o k.o k.s # assemble to a linkable ELF object gasm asm --format goobj -p pkg/path -o k.o k.s # Go object, consumed by go build gasm verify --ground-truth k.s # byte-for-byte vs go tool asm gasm verify --fuzz k.s # differential fuzz vs the go tool asm build gasm debug --func name k.s # interactive debugger gasm debug --func name --script cmds.txt --timeout 30s k.s # headless run gasm debug --func name --cover k.s # which labels did execution reach? gasm diff a.s b.s # compare machine code byte-for-byte gasm diff --map wideCopyAVX2=wideCopyAVX512 avx2.s avx512.s gasm profile k.s # show basic-block structure gasm audit-instructions # encoder vs go tool asm name diff gasm scaffold differential k.s # generate a differential test skeleton ``` Run `gasm --help` for the command overview and `gasm -h` for a command's flags. [docs/CLI.md](docs/CLI.md) is the full reference. ### Editor integration `gasm lsp` speaks the Language Server Protocol over standard input/output, so any LSP-capable editor can use it: point your editor's LSP client at the binary and associate it with `.s` files. Syntax highlighting is delivered as LSP semantic tokens, so no editor-specific grammar is required. The server infers the target architecture from the file-name suffix (`_amd64.s` / `_arm64.s` / `_riscv64.s` / `_loong64.s`). ## Development ```sh just install # download module dependencies just build # go vet + gofmt check, zero errors and zero warnings just test # full suite, race detector, 80 % coverage gate just fmt # gofmt the tree just gen # regenerate the instruction tables from the Go toolchain ``` See [CONTRIBUTING.md](CONTRIBUTING.md) for the development workflow and [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for setup details and every recipe. ## Documentation - [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md): components and data flow - [docs/CLI.md](docs/CLI.md): full command reference - [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md): development setup and recipes - [docs/DECISIONS.md](docs/DECISIONS.md): deferred design decisions - [CHANGELOG.md](CHANGELOG.md): release history ## Licence BSD-3-Clause — see [LICENSE](LICENSE). Copyright © 2026 [Petr Balvín](https://petrbalvin.org)