Assisted-by: GLM 5.3 Flash
This commit is contained in:
+86
-103
@@ -4,11 +4,13 @@ Repository: [sourcedock.dev/petrbalvin/gasm-devkit](https://sourcedock.dev/petrb
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Go** 1.27+ with `toolchain go1.27.0`
|
||||
- **Go** 1.27.1, the exact version the `go` directive in `go.mod` declares
|
||||
- **just**, the command runner; every task below is a just recipe
|
||||
- A Linux host on amd64, arm64, riscv64 or loong64: `gasm debug` needs ptrace
|
||||
and the JIT checks of `gasm verify` need executable memory
|
||||
- No external dependencies beyond the Go toolchain
|
||||
|
||||
## Quick Start
|
||||
## Setup
|
||||
|
||||
```sh
|
||||
git clone https://sourcedock.dev/petrbalvin/gasm-devkit.git
|
||||
@@ -17,105 +19,65 @@ just build # compile bin/gasm, zero errors and zero warnings
|
||||
just gates # build, fmt-check, vet, test, race: the definition of done
|
||||
```
|
||||
|
||||
## Just Recipes
|
||||
## Recipes
|
||||
|
||||
### `just build`
|
||||
Every recipe in the `justfile`, and what it does.
|
||||
|
||||
Compiles `bin/gasm` with `CGO_ENABLED=0` and stripped symbols. Zero
|
||||
errors, zero warnings. This is the minimum bar before any commit.
|
||||
|
||||
### `just gates`
|
||||
|
||||
The definition of done, in one command: `build`, `fmt-check`, `vet`,
|
||||
`test` and `race`, in that order. Run it once per task, never per edit;
|
||||
`just unit` is the one that runs after every edit.
|
||||
| Recipe | What it does |
|
||||
|---|---|
|
||||
| `just build` | compiles `bin/gasm` with `CGO_ENABLED=0` and stripped symbols; zero errors and zero warnings |
|
||||
| `just test` | the test gate: the suite with `-count=1`, the coverage profile and the 80 % floor |
|
||||
| `just race` | the same suite under the race detector; the expensive one, so it runs once, inside `gates` |
|
||||
| `just unit [packages] [run]` | fast, cached, scoped run for iterating: no race and no coverage, so an unchanged package reports instantly |
|
||||
| `just fuzz <target> <pkg> [fuzztime]` | time-boxed fuzz of one target; the package is required, because `go test -fuzz` refuses more than one |
|
||||
| `just bench [packages]` | benchmarks (`-benchmem -count=5`); on an idle machine only |
|
||||
| `just fmt` | formats the tree in place with `gofmt` |
|
||||
| `just fmt-check` | zero diff; prints nothing when everything is formatted, which is the shape the CI step wants |
|
||||
| `just vet` | both static gates: `go vet` and `go fix -diff` |
|
||||
| `just gates` | `build`, `fmt-check`, `vet`, `test` and `race`, in that order: the definition of done |
|
||||
| `just clean` | removes the build artefacts, `bin/` and `coverage.out` |
|
||||
| `just install` | builds, then copies the binary into `bindir` (`~/.local/bin`); `gasm debug` needs an installed binary, because it spawns the debuggee from `$PATH` |
|
||||
| `just uninstall` | removes the installed binary from `bindir` |
|
||||
| `just run` | runs the CLI with `go run -buildvcs=true`; the recipe takes no arguments, so flags go through the package instead |
|
||||
| `just dev` | the same as `run`; the project has no watcher to add |
|
||||
| `just gen` | regenerates the `arch` instruction tables from the Go toolchain source; not a gate |
|
||||
|
||||
### `just test`
|
||||
|
||||
```sh
|
||||
go test -count=1 -timeout 30m -coverprofile=coverage.out \
|
||||
-coverpkg=./arch/...,./asm/...,./ast/...,./disasm/...,./format/...,./lexer/...,./lint/...,./lsp/...,./parser/...,./token/...,./verify/... \
|
||||
./...
|
||||
go test -count=1 -timeout 10m -coverprofile=coverage.out \
|
||||
./arch/... ./asm/... ./ast/... ./disasm/... ./format/... ./lexer/... \
|
||||
./lint/... ./lsp/... ./parser/... ./token/... ./verify/...
|
||||
```
|
||||
|
||||
The whole suite runs (`-count=1`, so no cached pass counts), which keeps
|
||||
the packages that need hardware and the CLI glue under the gate. The
|
||||
coverage floor is computed over the product packages only (arch, asm,
|
||||
ast, disasm, format, lexer, lint, lsp, parser, token, verify; `debug`
|
||||
traces a live process and `cmd/gasm` is CLI glue) and fails if the total
|
||||
is below 80 %.
|
||||
The suite runs over the logic packages (`-count=1`, so no cached pass
|
||||
counts): arch, asm, ast, disasm, format, lexer, lint, lsp, parser,
|
||||
token, verify. `debug` traces a live process and `cmd/gasm` is thin CLI
|
||||
glue, so both sit outside the sweep, and a thin `cmd/` in it would drag
|
||||
the coverage total under the floor. The floor fails if the total is
|
||||
below 80 %. CI runs the same command with the same ten-minute bound, so
|
||||
the number is the same everywhere.
|
||||
|
||||
### `just race`
|
||||
|
||||
The same suite under the race detector. The expensive one, so it runs
|
||||
once, inside `gates`.
|
||||
|
||||
### `just unit`
|
||||
### `just run`
|
||||
|
||||
```sh
|
||||
just unit ./asm/ TestVexGroundTruth
|
||||
just run
|
||||
go run -buildvcs=true ./cmd/gasm lint kernel_amd64.s
|
||||
go run -buildvcs=true ./cmd/gasm verify --ground-truth kernel_amd64.s
|
||||
```
|
||||
|
||||
Fast, cached, scoped run for iterating: no race, no coverage, so an
|
||||
unchanged package reports instantly.
|
||||
|
||||
### `just fuzz <target> <pkg>`
|
||||
|
||||
Time-boxed fuzz of one target in one package; the package is required,
|
||||
because `go test -fuzz` refuses more than one. Seed corpora run as plain
|
||||
tests in `unit` and `test`. Never a gate.
|
||||
|
||||
### `just bench`
|
||||
|
||||
Benchmarks (`-benchmem -count=5`). On an idle machine only.
|
||||
|
||||
### `just fmt`, `just fmt-check`, `just vet`
|
||||
|
||||
`fmt` formats in place. `fmt-check` prints nothing when everything is
|
||||
formatted, which is the shape the CI step wants. `vet` runs both static
|
||||
gates: `go vet` and `go fix -diff`.
|
||||
|
||||
### `just run -- <args>`
|
||||
|
||||
Runs the CLI via `go run -buildvcs=true`:
|
||||
|
||||
```sh
|
||||
just run -- lint kernel_amd64.s
|
||||
just run -- fmt -w kernel_amd64.s
|
||||
just run -- verify --ground-truth kernel_amd64.s
|
||||
```
|
||||
|
||||
### `just install`
|
||||
|
||||
Builds and copies the `gasm` binary into `~/.local/bin` (`BINDIR`
|
||||
overrides the destination).
|
||||
|
||||
### `just uninstall`, `just clean`
|
||||
|
||||
`uninstall` removes the installed binary from `bindir`. `clean` removes
|
||||
the build artefacts: `bin/` and `coverage.out`.
|
||||
|
||||
## Version reporting
|
||||
|
||||
The version is never injected. `gasm --version` prints what the
|
||||
toolchain recorded in the build information: the tag on a tagged
|
||||
checkout, a pseudo-version naming the commit below one, `+dirty` on a
|
||||
dirty tree, and `(devel)` outside version control. There is no
|
||||
`-ldflags "-X"` anywhere and no version constant in the source.
|
||||
The flag on `go run` is there because it does not stamp the build otherwise,
|
||||
which `--version` would then report as `(devel)`.
|
||||
|
||||
### `just gen`
|
||||
|
||||
Regenerates the architecture instruction tables in `arch/` by parsing
|
||||
the Go toolchain's own assembler source
|
||||
Regenerates the architecture instruction tables in `arch/` by parsing the Go
|
||||
toolchain's own assembler source
|
||||
(`$GOROOT/src/cmd/internal/obj/<arch>/anames.go`). Requires a Go
|
||||
installation. Output is committed, with no runtime dependency on the
|
||||
toolchain.
|
||||
|
||||
### `just uninstall`
|
||||
|
||||
Removes `coverage.out`, the `gasm` binary, and `*.test` artefacts.
|
||||
|
||||
## Running Individual Tests
|
||||
## Running a single test
|
||||
|
||||
```sh
|
||||
go test -run TestVexGroundTruth ./asm/
|
||||
@@ -124,32 +86,53 @@ go test -run TestGOObjectLinkAndRun ./asm/
|
||||
go test -run TestFuzzWideCopy ./verify/
|
||||
```
|
||||
|
||||
## Debugger Note
|
||||
Add `-v` for the sub-test names, and `-race` when the change touches
|
||||
concurrency. `-count=1` defeats the test cache when a result looks stale.
|
||||
|
||||
`gasm debug` spawns a child process from the binary on `$PATH`. It does
|
||||
not work with `go run`; install first:
|
||||
## Coverage
|
||||
|
||||
```sh
|
||||
just install
|
||||
gasm debug --func decodeBlockAVX2 path/to/kernel_amd64.s
|
||||
just test
|
||||
go tool cover -func=coverage.out
|
||||
```
|
||||
|
||||
## Project Layout
|
||||
The `total:` line is the number that matters, and it stays at 80 percent or
|
||||
more.
|
||||
|
||||
## Debugging the build
|
||||
|
||||
```sh
|
||||
go build -gcflags='-m' ./... # inlining decisions
|
||||
go build -gcflags='-S' ./... # what the compiler generated
|
||||
go tool asm -S kernel_amd64.s # how the toolchain's assembler encodes a kernel
|
||||
gasm dis kernel_amd64.s # what gasm makes of the same kernel
|
||||
gasm tokens kernel_amd64.s # the token stream
|
||||
gasm profile kernel_amd64.s # the basic blocks of each function
|
||||
```
|
||||
cmd/gasm/ CLI entry point (subcommands)
|
||||
token/ Lexical token kinds and positions
|
||||
lexer/ Hand-written scanner
|
||||
ast/ Abstract syntax tree
|
||||
parser/ Line-oriented parser
|
||||
arch/ Register and instruction tables (generated)
|
||||
lint/ Static analysis rules
|
||||
format/ Canonical formatter
|
||||
lsp/ Language Server Protocol server
|
||||
asm/ Standalone assembler, encoder, object emitters
|
||||
verify/ JIT execution, differential testing, ABI checks
|
||||
debug/ Interactive ptrace debugger (all four architectures)
|
||||
_gen/ Instruction table generator
|
||||
testdata/ Test fixtures
|
||||
docs/ Architecture, development, CLI reference
|
||||
```
|
||||
|
||||
`gasm verify --ground-truth` is the differential check that ties the two
|
||||
together: it compares gasm's bytes with `go tool asm`'s, with the relocation
|
||||
sites masked, so an encoding drift shows up as a byte difference rather than a
|
||||
crash later.
|
||||
|
||||
## Continuous integration
|
||||
|
||||
Workflows live in `.gitea/workflows/` and run on the project's own runners:
|
||||
Test on a push or pull request to `development`, race dispatched by hand, and
|
||||
the release on a `v*` tag. They are written by hand rather than through
|
||||
`just`, but they enforce the same set of gates minus the race detector, which
|
||||
the shared runner cannot afford on a push; a green `just gates` locally is
|
||||
therefore the fastest way to a green pipeline.
|
||||
|
||||
## Releases
|
||||
|
||||
Releases are cut by merging `development` into `main` and tagging `vX.Y.Z`,
|
||||
which triggers the release workflow: it builds the portable Linux targets,
|
||||
takes the notes from the matching `CHANGELOG.md` section and uploads the
|
||||
assets.
|
||||
|
||||
The version is never injected. `gasm --version` prints what the
|
||||
toolchain recorded in the build information: the tag on a tagged
|
||||
checkout, a pseudo-version naming the commit below one, `+dirty` on a
|
||||
dirty tree, and `(devel)` outside version control. There is no
|
||||
`-ldflags "-X"` anywhere and no version constant in the source.
|
||||
|
||||
Reference in New Issue
Block a user