diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a0e94e..ce9e107 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,59 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [development] -### Fixed - -- **The operand forms GOROOT writes.** Numeric PC-relative jumps - (`JEQ 2(PC)`, the park loop `JMP 0(PC)`) resolve with the toolchain's - own instruction counting and fold jump-to-jump chains exactly as its - branch optimiser does; symbol immediates (`MOVQ $sym(SB), AX`) - assemble to the toolchain's RIP-relative LEA with an R_PCREL - relocation; negated constant expressions in operands (`ADJSP - $-(REGS - 8)`, the shape the cgo ABI macros write) fold; the TLS - access pair assembles as the toolchain's one-instruction form (the - bare `MOVQ TLS, r` load nops out and `off(r)(TLS*1)` folds to the - segment-prefixed absolute whose disp32 carries the R_TLSLE - relocation, per-GOOS); arm64 accepts the bare-register indirect branch - (`BL R9` beside `BL (R9)`, both BLR) and the zero-immediate store - (`MOVD $0, mem` through the zero register, rejecting non-zero immediates - as the toolchain does); `PCALIGN` now aligns on amd64, padding with the - toolchain's greedy single-instruction NOPs; the segment-absolute forms - (`MOVQ 0x30(GS), AX` and the store direction) and the absolute - crash-store (`MOVL $0xf1, 0xf1`) encode; and `gasm asm` predefines - the `GOARCH_` and `GOOS_` macros the go command passes to - `go tool asm`, so GOROOT headers' `#ifdef GOARCH_amd64` platform blocks - (`go_tls.h`'s `get_tls` and friends) select as intended. The GOROOT - corpus measure moves to 291 of 353 files assembling for every target - architecture (82.4 %), 97.4 % of the real-code corpus, from 70.8 % - and 82.2 %. - ### Added -- **Per-architecture reference pages.** [docs/asm/](docs/asm/README.md) - gains AMD64, ARM64, RISCV64 and LOONG64: the register files and the - roles the ABI fixes, addressing, operand order with every special form, - constants and materialisation, alignment, fences and the relocations - each target emits. An instruction inventory appendix per architecture - is generated from the toolchain's own tables by `just gen`, and the - regenerated tables recognise 147 more mnemonics than the previous - release carried (arm64 107, riscv64 31, loong64 9). -- **The Plan 9 assembly language reference.** [docs/asm/](docs/asm/README.md) - opens the complete language reference with its common core: the lexicon, - statement structure and constant expressions, the operand grammar with - the pseudo-registers and symbol naming, the directives and the function - flag vocabulary, preprocessing with `#define` and `#include`, and the - Go-embedded layer (ABI0, prototypes, `go_asm.h`, `funcdata.h` and the - runtime contract). Every claim is verified against `go tool asm` of - Go 1.27.1 and gasm's differential tests; the per-architecture pages and - generated instruction appendices follow. -- **GOOBJ format specification.** [docs/GOOBJ.md](docs/GOOBJ.md) - documents the Go object file format in full: both containers, the 96 - byte header and all 19 blocks, every structure with its byte - offsets, symbol kinds and flag bits, all 106 relocation types with - the weak variants, aux symbols, the FuncInfo payload, the pc-value - table encoding, the content hashes and the builtin table, all - verified byte for byte against objects produced by Go 1.27.1's own - tools. +- **The go_asm.h generator.** `gasm asm` generates the package's go_asm.h + itself when an assembly file includes it: the Go files beside the source + are type-checked for the target architecture and the constants and field + offsets become assembler defines, so package-context files assemble with + no compiler and no `go build` in the loop. `-GOOS` selects the + type-checking GOOS for GOOS-specific files, and the corpus audit derives + the GOOS from the file name. +- **ELF data relocations on arm64, riscv64 and loong64.** `gasm asm + --format elf` emits `.rela.data` for symbol-valued DATA initialisers on + every architecture (amd64 carried them already), so standalone ELF + objects link on all four targets. +- **Corpus failure listing.** `gasm audit-instructions --corpus --list` + prints every failing file with its failure reason, per architecture, + instead of one representative file per reason. +- **DATA with symbol values and relaxed symbol spellings.** DATA + initialisers accept `$symbol(SB)` values, laid down as an absolute + relocation at the data field (GOOBJ on all four architectures and ELF + on all four as of this release), and U+2215 is accepted inside symbol + package paths. - **Macro expansion and include splicing.** `gasm asm`, `gasm diff` and `gasm audit-instructions` now preprocess assembly the way the toolchain does: object and parameterised `#define` macros expand at @@ -100,21 +68,77 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ranges, index-only VSIB memory operands and bare trailing immediates; macro substitution reaches parameters used with element suffixes (`A.S4`), and `;` separates statements in plain files. -- **`gasm asm -GOOS`.** The go_asm.h generator type-checks per target - GOOS, so the darwin-only and windows-only runtime files assemble with - their own defines; the corpus audit derives the GOOS from the file - name. DATA initialisers accept `$symbol(SB)` values (an absolute - relocation at the data field, GOOBJ on all four architectures and ELF - on amd64, arm64, riscv64 and loong64), and U+2215 is accepted inside - symbol package paths. -- **The corpus audit measures honestly.** Files named for Go ports gasm - does not target (arm, 386, s390x, ...) are no longer attempted for the - four supported architectures (no supported build compiles them), and - the headline rate is reported over attemptable files: 136 of 433 on - the full corpus (31.4 %), 135 of 383 on real code (35.2 %), from the - 127 that the previous release measured. The probe battery that - decides encodability gained the operand shapes the new families use. -- +- **Per-architecture reference pages.** [docs/asm/](docs/asm/README.md) + gains AMD64, ARM64, RISCV64 and LOONG64: the register files and the + roles the ABI fixes, addressing, operand order with every special form, + constants and materialisation, alignment, fences and the relocations + each target emits. An instruction inventory appendix per architecture + is generated from the toolchain's own tables by `just gen`, and the + regenerated tables recognise 147 more mnemonics than the previous + release carried (arm64 107, riscv64 31, loong64 9). +- **The Plan 9 assembly language reference.** [docs/asm/](docs/asm/README.md) + opens the complete language reference with its common core: the lexicon, + statement structure and constant expressions, the operand grammar with + the pseudo-registers and symbol naming, the directives and the function + flag vocabulary, preprocessing with `#define` and `#include`, and the + Go-embedded layer (ABI0, prototypes, `go_asm.h`, `funcdata.h` and the + runtime contract). Every claim is verified against `go tool asm` of + Go 1.27.1 and gasm's differential tests; the per-architecture pages and + generated instruction appendices follow. +- **GOOBJ format specification.** [docs/GOOBJ.md](docs/GOOBJ.md) + documents the Go object file format in full: both containers, the 96 + byte header and all 19 blocks, every structure with its byte + offsets, symbol kinds and flag bits, all 106 relocation types with + the weak variants, aux symbols, the FuncInfo payload, the pc-value + table encoding, the content hashes and the builtin table, all + verified byte for byte against objects produced by Go 1.27.1's own + tools. + +### Changed + +- **The corpus audit measures like a build.** Files named for a Go port + gasm does not target (arm, 386, s390x, ...) are never attempted, because + no supported build compiles them; the GOOS comes from the file name; and + each target's go_asm.h is generated on the fly. The headline is reported + over attemptable files: 291 of 353 on the full corpus (82.4 %) assemble + for every target architecture and 295 of 303 on real code (97.4 %), + against 108 of 627 over all files (17.2 %) that the previous release + measured. + +### Fixed + +- **The operand forms GOROOT writes.** Numeric PC-relative jumps + (`JEQ 2(PC)`, the park loop `JMP 0(PC)`) resolve with the toolchain's + own instruction counting and fold jump-to-jump chains exactly as its + branch optimiser does; symbol immediates (`MOVQ $sym(SB), AX`) + assemble to the toolchain's RIP-relative LEA with an R_PCREL + relocation; negated constant expressions in operands (`ADJSP + $-(REGS - 8)`, the shape the cgo ABI macros write) fold; the immediate + multiply (`IMULQ $1000000000, AX`) encodes with the toolchain's + 0x69/0x6B selection; the TLS access pair assembles as the toolchain's + one-instruction form (the bare `MOVQ TLS, r` load nops out and + `off(r)(TLS*1)` folds to the segment-prefixed absolute whose disp32 + carries the R_TLSLE relocation, per-GOOS); arm64 accepts the + bare-register indirect branch (`BL R9` beside `BL (R9)`, both BLR) and + the zero-immediate store (`MOVD $0, mem` through the zero register, + rejecting non-zero immediates as the toolchain does); `PCALIGN` now + aligns on amd64, padding with the toolchain's greedy + single-instruction NOPs; the segment-absolute forms (`MOVQ 0x30(GS), + AX` and the store direction) and the absolute crash-store + (`MOVL $0xf1, 0xf1`) encode; and `gasm asm` predefines the + `GOARCH_` and `GOOS_` macros the go command passes to + `go tool asm`, so GOROOT headers' `#ifdef GOARCH_amd64` platform + blocks (`go_tls.h`'s `get_tls` and friends) select as intended. The + GOROOT corpus measure moves to 291 of 353 files assembling for every + target architecture (82.4 %), 97.4 % of the real-code corpus, from + 70.8 % and 82.2 %. +- **Tool corrections across the pipeline.** The formatter keeps square + brackets in SIMD operands, statement separators and canonical macro + bodies; the linter drops false positives on shift counts, SETcc + spellings and ABIInternal references; the lexer treats a trailing + carriage return as a line end so comment text stays idempotent; and + arm64 rejects bare BTI with a diagnostic while accepting the full + family. ## [0.34.0] - 2026-09-20 diff --git a/README.md b/README.md index 4937744..5151248 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,10 @@ to give that syntax the tooling it deserves. GOOBJ format, which needs the installed toolchain and which `go build` consumes in place of the toolchain's output. Framed functions get the stack-split guard and the morestack block, byte-identical to the - toolchain's, so split functions link too. + toolchain's, so split functions link too. The assembler preprocesses + like the toolchain (`#define`, `#include` with `-I`, `#ifdef`), generates + `go_asm.h` from the package's Go files, and carries `PCALIGN`, the + `LOCK`/`REP` prefixes and the literal-data pseudo-ops. - **Disassembler.** `gasm dis` lists a `.s` file's functions at their real offsets after assembling, or disassembles raw bytes from a file or stdin. - **Dynamic verification.** `gasm verify` JIT-loads assembled functions into @@ -110,9 +113,9 @@ Four architectures, the four that matter in practice: | 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 | +| ARM64 | `arm64` | `_arm64.s` | 645 + common opcodes | +| RISC-V | `riscv64` | `_riscv64.s` | 992 + common opcodes | +| LoongArch | `loong64` | `_loong64.s` | 808 + common opcodes | "Common opcodes" are the instructions shared by every architecture (`RET`, `JMP`, `NOP`, `CALL`, `TEXT`, `FUNCDATA`, `PCDATA`, ...). AMD64 additionally @@ -124,8 +127,8 @@ can emit today is narrower, and a recognised but unencodable instruction is reported as an explicit error, never as a wrong byte. The same measurement runs over GOROOT's whole assembly corpus: -`gasm audit-instructions --corpus` reports 136 of 433 attemptable files -(31.4 %) assembling for every target architecture today (files named for +`gasm audit-instructions --corpus` reports 291 of 353 attemptable files +(82.4 %) assembling for every target architecture today (files named for other Go ports are counted but never attempted), with the top failure reasons per architecture; the number moves with every release. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 1826b32..d353abd 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -119,6 +119,20 @@ identifier is a register or a label is an *architecture* question, so it is left to `arch` and resolved in the lint/lsp layers. This keeps the parser arch-agnostic and its output deterministic. +### Optional preprocessing + +With `Options{Expand: true}` the parser runs a pre-parse pass +(`preproc.go`) that splices `#include` files (the source directory, then the +`-I` directories), expands object and parameterised `#define` macros, +applies `#undef` and the `#ifdef`/`#ifndef`/`#else`/`#endif` family, and +folds constant expressions left in operands. The go command's platform +macros (`GOARCH_`, `GOOS_`) arrive through `Options.Predefines`. +The assembly path (`asm`, `diff`, `audit`) expands; `lint`, `fmt` and the +language server read the raw file. The command layer adds the go_asm.h +generator (`asmhdr.go`): a file that includes go_asm.h gets the package's +defines type-checked out of its Go files for the target architecture and +GOOS, with no compiler in the loop. + ### `arch` Register files are generated programmatically (the regular `R8`-`R15`,