docs: sync README, CHANGELOG and docs with the current state

This commit is contained in:
2026-08-30 10:44:18 +02:00
parent 6c1c8d9d96
commit 56f8babbce
7 changed files with 407 additions and 263 deletions
+119 -111
View File
@@ -1,149 +1,157 @@
# gasm-devkit
Developer tooling for **GAsm** — Go's built-in Plan 9 assembler.
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,
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.
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.
gasm-devkit is the missing toolkit. It is a single, self-contained binary —
`gasm` — that brings proper developer tooling to Plan 9 assembly:
## Features
```
gasm tokens dump the lexical token stream
gasm parse parse and report syntax errors
gasm fmt canonicalise formatting (gofmt for assembly)
gasm lint static checks
gasm lsp language server (completion, hover, symbols, diagnostics, highlighting)
gasm asm standalone assembler
gasm verify dynamic analysis & verification
gasm debug source-level debugger
gasm diff compare machine code of two .s files
gasm profile show basic-block structure of functions
```
- **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 17 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,
diagnostics, semantic-token highlighting, go-to-definition, find references,
rename, formatting, inlay hints, code actions, signature help, document
highlights and workspace symbol search 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 support
gasm-devkit targets every architecture Go's assembler speaks. The instruction
tables are **generated from the Go toolchain's own assembler source**
(`cmd/internal/obj/<arch>`), so gasm-devkit recognises *every* mnemonic the
real assembler accepts — not a hand-maintained subset that drifts and rots.
| 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 |
| 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
`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.
...) 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.
## Supported Platforms
## Install
The toolkit runs on Linux. All four Linux architectures are supported as
hosts — amd64, arm64, riscv64 and loong64 — and the release matrix
cross-compiles the same four targets.
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):
**FreeBSD support is planned for a future release.**
```sh
go install sourcedock.dev/petrbalvin/gasm-devkit/cmd/gasm@latest
```
## Principles
Or from a repository checkout, with the development version stamped:
- **Pure Go and GAsm only.** No C, no cgo, no external toolchains, no native
binaries, no JavaScript runtimes. The parser is hand-written; there is no
parser generator.
- **Self-contained.** The toolkit's production code depends only on the
standard library; one binary, no runtime data files. The single module
dependency, `golang.org/x/arch`, is used **only in tests** to validate the
instruction encoder by round-trip decoding — it is never linked into the
`gasm` binary.
- **Linux-only.** Runs natively on amd64, arm64, riscv64 and loong64 Linux
hosts; the release matrix cross-compiles the same four targets. Latest
stable Go only.
- **No vendor lock-in.** The integration surface is the Language Server
Protocol and a command-line interface — both open standards. No cloud
service, no proprietary API, no dependence on any one editor's internals.
- **Complete and verifiable.** Instruction coverage is generated from the
assembler's own source and regenerated on demand, so it cannot silently fall
behind the toolchain.
## Components
| Package | Purpose |
|---------|---------|
| `token` | Lexical token kinds and source positions. |
| `lexer` | Hand-written scanner for Plan 9 assembly. |
| `ast` | The abstract syntax tree. |
| `parser` | Line-oriented, error-tolerant parser producing the AST. |
| `arch` | amd64, arm64, riscv64 and loong64 register files and instruction tables. |
| `lint` | Conservative static checks (13 rules including unused-label, invalid-textflag, stack-imbalance). |
| `s` | A canonical formatter — `gofmt` for assembly. |
| `asm` | The standalone assembler: all four architecture encoders, linker, object-file emitters (ELF with DWARF5, GOOBJ). |
| `verify` | JIT execution substrate for dynamic analysis, combined ABI+fuzz differential testing. Assembly trampolines for all four architectures. |
| `debug` | Interactive ptrace debugger for all four architectures: single-stepping, breakpoints, hardware watchpoints, register and memory inspection. |
| `lsp` | Language Server Protocol server: completion, hover, symbols, diagnostics, semantic tokens, find references, rename, formatting, inlay hints. |
| `cmd/gasm` | The `gasm` binary tying it all together. |
| `_gen` | The generator that rebuilds the instruction tables from the Go toolchain. |
See [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) for the design rationale and
data flow, and [`docs/DECISIONS.md`](docs/DECISIONS.md) for design decisions
deliberately postponed (with the analysis needed to pick them up again).
```sh
just install-bin
```
## Quick start
```sh
just install # download dependencies (there are none)
just build # go vet + gofmt check — zero errors, 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
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
```
Install the binary and use it:
## Usage
```sh
just install-bin # installs gasm into $GOBIN
gasm --help # overview of commands and flags
gasm tokens kernel_amd64.s # dump the token stream
gasm parse kernel_amd64.s # parse, report syntax errors
gasm fmt -w kernel_amd64.s # canonicalise in place
gasm fmt # reformat every .s below here, like go fmt
gasm lint *.s # static checks
gasm asm --format elf -o k.o k.s # assemble to a linkable ELF object
gasm verify kernel_amd64.s # JIT-load and report functions
gasm verify --ground-truth k.s # byte-for-byte vs go tool asm
gasm verify --call decodeBlockAVX2 --buf src:64:hex...,dst:256:zero k.s
gasm verify --call sumTo --args n=57,base=0x1f k.s # scalar arguments
gasm debug --func name k.s # interactive debugger
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 audit-instructions # encoder vs go tool asm name diff
gasm scaffold differential k.s # generate a differential test skeleton
gasm diff a.s b.s # compare machine code byte-for-byte
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 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
```
See [CONTRIBUTING.md](CONTRIBUTING.md) for the full development workflow,
[docs/CLI.md](docs/CLI.md) for the command reference, and
[docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for setup and recipes.
Run `gasm --help` for the command overview and `gasm <command> -h` for a
command's flags. [docs/CLI.md](docs/CLI.md) is the full reference.
## Editor integration
### 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
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
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`).
## License
## 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).
BSD-3-Clause — see [LICENSE](LICENSE).
Copyright © 2026 [Petr Balvín](https://petrbalvin.org)