diff --git a/CHANGELOG.md b/CHANGELOG.md index df4b929..1f1872c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 states the commit trailer form, the one-logical-change rule and the licence header rule. The repository's own assembly (the `verify` trampolines and the test kernels) is in `gasm fmt` canonical form. +- **The README states the project's purpose and status.** It opens with + a warning that the tool is an experiment under active development, + version 0.x.x, free to change without warning, with 1.0.0 far off, + and already in active use on real assembly work. It describes both + goals (tooling for Plan 9 assembly, and Plan 9 assembly outside the + Go toolchain), argues the case for the syntax in a new Why Plan 9 + assembly section, and carries a Direction section: extended + instruction support, full GOOBJ and ELF compilation, Linux and + FreeBSD, and the four architectures. ### Fixed diff --git a/README.md b/README.md index e543930..f1f7b0e 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,63 @@ -# gasm-devkit +# Plan 9 assembly tooling, inside and outside Go -Developer tooling for **GAsm**, Go's built-in Plan 9 assembler. +> **Warning: this is an experiment.** gasm-devkit is under active +> development and is not stable. The version is 0.x.x: commands, flags, +> output formats and behaviour can change without warning at any time. +> A 1.0.0 release is light years away. Nothing in this document is a +> stability promise. For all of that, this is not a paper project: gasm +> is already in active use and is tested on real assembly work. -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. +**GAsm** is Go's Plan 9 assembler, and Go ships it without tooling: +there is no formatter, no linter, no static analyser, 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 serves both purposes. + +- **Help develop Plan 9 assembly.** Formatting, linting, disassembly, + dynamic verification, a source-level debugger and a language server, + for `.s` files in Go programs. +- **Use Plan 9 assembly outside the Go toolchain.** `gasm asm` encodes + on its own, with no Go installation in the loop, 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. + +## Why Plan 9 assembly + +Plan 9 assembly is the quiet triumph of the field. One syntax across +every architecture Go builds for: the same source-first operand order, +the same four pseudo-registers, the same frame convention, whether the +target is x86, ARM, RISC-V or LoongArch. Learn it once and you can +read a kernel on any of them. + +Compare the alternatives. Intel syntax and AT&T syntax disagree on the +one question every instruction answers, which operand is the source +and which is the destination, so half the world writes it one way, +half the other, and every assembly programmer carries both in their +head forever. GNU as settles the argument with directives that switch +dialects mid-file (`.intel_syntax noprefix`), a percent sign on every +register and a dollar on every immediate: punctuation that carries +nothing the operand order did not already say. And the x86 family +fragments again underneath: NASM is not MASM is not GAS, each with its +own directive zoo and macro language, so every project picks a dialect +and every reader learns a different one by accident. + +Plan 9 assembly has none of it. Registers are bare names. Memory is +one notation, `offset(base)`, extended by an index and a scale when +the instruction needs it. Arguments arrive named and offset-checked: +`x+0(FP)` is the argument x, on every architecture, and `go vet` +polices the offsets against the Go prototype. + +```text +AT&T (GNU as): movq %rax, -16(%rbp) +Plan 9 (Go): MOVQ AX, total-16(SP) +``` + +The same lines, but only one of them tells you what the number is for. +The syntax is uppercase, regular and boring, which is the highest +compliment a language for machine code can earn. gasm-devkit exists +to give that syntax the tooling it deserves. ## Features @@ -47,12 +97,11 @@ developer tooling to Plan 9 assembly on amd64, arm64, riscv64 and loong64. 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 +Four architectures, the four that matter in practice: + | Architecture | GOARCH | File suffix | Instructions recognised | |--------------|-------------|--------------|---------------------------------------------| | AMD64 | `amd64` | `_amd64.s` | 1600 + common opcodes + traditional aliases | @@ -63,9 +112,38 @@ developer tooling to Plan 9 assembly on amd64, arm64, riscv64 and loong64. "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. +...) that the assembler accepts as aliases. The tables are generated from +the Go toolchain's own assembler source (`just gen` refreshes them), so +every mnemonic the real assembler accepts is recognised; what the encoder +can emit today is narrower, and a recognised but unencodable instruction is +reported as an explicit error, never as a wrong byte. + +## Direction + +The plan, in the order it is being worked: + +- **Extended instruction support.** Two layers. First, encoding + coverage for every mnemonic the Go toolchain itself accepts, closed in + order of how often real code needs each instruction; + `gasm audit-instructions` measures the gap. Second, the larger work: + an extended instruction set the toolchain does not know at all. The + toolchain-derived tables stay generated and untouched; only the + extended instructions are hand-maintained, with their own spellings + and encoders, verified by execution on real hardware because the + toolchain offers no ground truth to compare against. The gaps exist + on every architecture, amd64 included. +- **Full GOOBJ and ELF compilation.** The destination is a complete, + standalone compilation path: linkable ELF objects for consumers outside + Go, and GOOBJ objects that `go build` links directly. Through GOOBJ, a + Go program will be able to use machine instructions that the Go + toolchain itself does not support; through ELF, Plan 9 assembly becomes + usable outside Go entirely. +- **Platforms: Linux and FreeBSD.** Linux is supported today on all four + architectures and is where the binary builds. FreeBSD follows: the + JIT's executable-memory mapping and the ptrace debugger layer are the + two pieces of porting work. Other unix systems may follow those two. +- **Four architectures, no more.** amd64, arm64, riscv64 and loong64. + No others are planned. ## Install