docs: changelog entries for the review fixes
Test / test (push) Successful in 2m4s

Assisted-by: GLM 5.3
This commit is contained in:
2026-09-19 23:49:27 +02:00
parent e3b35bb817
commit a6f3828c02
+154
View File
@@ -109,6 +109,160 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
lines entered the alignment width computation, and rendered `/ *`, lines entered the alignment width computation, and rendered `/ *`,
`> >` sequences re-lexed as comments and shifts. The label, width and `> >` sequences re-lexed as comments and shifts. The label, width and
spacing rules now agree between passes. spacing rules now agree between passes.
- **`gasm fmt` deleted the `|` separators from TEXT and GLOBL flag
lists.** The lexer had no token for `|`, the formatter dropped the
resulting illegal token, and an in-place format silently rewrote
`NOSPLIT|DUPOK` as `NOSPLIT DUPOK`, which the Go assembler rejects.
The bars now round-trip byte-identically, and `·foo<ABIInternal>(SB)`
parses its ABI marker instead of swallowing `ABIInternal` and `SB`
into the flags, which produced false lint warnings on the standard
runtime spelling.
- **A malformed TEXT declaration crashed `gasm lint` and the language
server.** A TEXT line without a symbol left a nil name that lint and
the LSP dereferenced; both now carry on with a diagnostic. A branch
to a label at the end of a function body panicked the liveness
analysis the same way. A real NUL byte truncated the token stream
(everything after it was dropped); it is an illegal token now, invalid
UTF-8 no longer inflates byte offsets, and CRLF files format to
uniform LF.
- **A frameless amd64 function containing a CALL read its arguments from
the wrong stack slot.** The forced base-pointer frame shifted
FP references by eight bytes (`x+0(FP)` resolved to SP+0x18 where the
toolchain emits SP+0x10), so such functions loaded garbage. The
class-2 stack guard had the sibling defect: whenever the underflow
branch relaxed to its 32-bit form, its displacement ran four bytes
past the target and into the morestack CALL.
- **Immediate operands wrapped silently on amd64.** Shift counts,
immediates beyond the operand's width and displacements beyond int32
truncated without a diagnostic (`SHLQ $300` assembled as `$44`); they
are range-checked now, matching `go tool asm`. EVEX scalar moves
(`VMOVSS Z1, Z2`) accepted forms the toolchain rejects and emitted
invalid encodings; `PUSHW`/`POPW` emit the 0x66-prefixed forms the
toolchain emits; `PUSHL` is rejected as illegal in 64-bit mode; a bare
zero-operand `JE` reports a diagnostic instead of panicking.
- **The arm64 shift and divide instructions encoded entirely different
operations.** `LSL`, `LSR`, `ASR` and `ROR`, immediate and register
forms, all encoded as `ORR`; `SDIV`/`UDIV` sat in the wrong opcode
space; `MADD`/`MSUB` never encoded the accumulate operand and silently
read X0 for it. All now match the toolchain byte for byte (new
differential kernels cover shifts, divides and multiplies), MADD
takes its four operands in the toolchain's order, and shift amounts at
or above the operand width are rejected.
- **arm64 multi-chunk immediates corrupted every branch that followed
them.** The size pass and the emitter disagreed on the expansion of
constants with three or more non-zero 16-bit chunks and of `MOVW $-1`,
so later label displacements were computed against the wrong offsets.
The size now comes from the encoder itself. Large-frame stack guards
(frames from roughly 64 KiB) branched to the wrong morestack entry,
and the pcsp and DWARF CFA boundaries for materialised large frames
are computed from the real prologue word counts.
- **arm64 immediates and addressing wrapped instead of erroring.**
Constants beyond the encodable range (`ADD $0x100000000`) wrapped to
zero, memory offsets wrapped at 2^31, exclusive and atomic accesses
silently ignored their offsets (`LDXR 8(R1)` read `[R1]`), and large
register-based offsets were routed through SP instead of the operand's
base. All four now either encode correctly or produce diagnostics.
- **riscv64 compressed stores with certain offsets wrote to the wrong
address.** The C.SD/C.SW/C.FSD immediate pattern dropped one bit, so
any register-relative store with offset bit 4 or 5 set targeted a
different address than the same-index load beside it. `FENCE`
assembled as `fence 0,0` instead of `fence iorw, iorw`. Branch and
jump displacements beyond ±4 KiB / ±1 MiB wrapped silently; they are
diagnostics now. The GOROOT width spellings (`MOVW 4(SP), X9`)
compress to their C.LW/C.SW forms exactly as the toolchain lowers
them, restoring byte parity for those shapes.
- **loong64 `MOVW $c, Fd` wrote a general register.** The immediate was
routed to the GPR of the F register's number (`MOVW $2, F4` clobbered
argument register R4), and the correct R30 + `movgr2fr.w` sequence was
unreachable. Two-operand `BLTU R4, label` encoded as `beqz`
(sometimes-taken where the toolchain's form is never-taken), and
out-of-range FP constants and BSTRINS/BSTRPICK bit numbers wrapped
silently; all are corrected or diagnosed.
- **ELF objects carried wrong relocation records.** The amd64
stack-guard TLS load relocated as R_X86_64_PC32 against the null
symbol (every non-NOSPLIT object mislinked); arm64 SB references
applied HI21 twice instead of the HI21/LO12 pair; riscv64 PCREL_LO12
referenced the target instead of its AUIPC site, which the system
linker rejects; riscv64 and loong64 e_flags declared the soft-float
ABI, so standard linkers refused the merge.
- **ELF DWARF was unparseable.** Eight abbrev-table constants were
wrong, the version-5 line header carried DWARF2-shaped tables, the
section count omitted `.debug_frame` (it sat past the section table,
invisible to every tool), the CIE hardcoded one architecture's
CFA and return-address registers for all four, and no DWARF address
was ever relocated: the `.rela.debug_info` and `.rela.debug_line`
records were computed and then discarded, so every address stayed
zero after linking. The tables parse in readelf, the registers are
per-architecture, `.rela.debug_info`, `.rela.debug_line` and
`.rela.debug_frame` are emitted, and addresses resolve after the
link; a data-only file emits a valid object instead of panicking, and
the DWARF records the real source path.
- **GOOBJ cross-package references resolved against the wrong object.**
External package indices were zero-based against a table that
reserves zero for the dummy invalid package, and symbol indices
ignored the hashed definition blocks between the sections, so a
reference into the first external package could bind to whatever
object the loader saw first. An end-to-end cross-package link pins
the chain. arm64 ADRP pairs now emit the toolchain's single 8-byte
relocation (the previous twin 4-byte records were a hard link error),
and symbols no longer claim the linkname flag the toolchain reserves
for `//go:linkname` declarations.
- **The arm64 JIT trampolines saved a scratch register as the stack
pointer.** `enterJIT` and its checked twin stored R3, a plain
caller-saved register on arm64, and restored RSP from it, so the
first JIT call would have returned to a garbage stack. The loong64
trampoline hands its leave address through the raw-symbol pattern the
arm64 one uses, avoiding the ABI wrapper's prologue.
- **The checked-ABI report flagged legal frames.** The red-zone canary
sat 64 bytes below the entry stack, so any kernel with a larger
declared frame reported "stack below SP written"; the guard now sizes
itself from the kernel's frame. The amd64 JIT tests are gated to
amd64 hosts (the suite previously SIGILL-crashed on the other three
architectures), fuzz signatures wider than the TEXT frame report
instead of panicking, `--buf` specifications are validated strictly
(a typo no longer verifies against a zeroed buffer), and ABI0
parameter sizes cover `string` and `complex` correctly.
- **amd64 hardware watchpoints never armed.** The debug registers were
poked at offsets inside `user_regs_struct`, corrupting five general
registers while the REPL reported success; they now use the real
u_debugreg window and stop on the watched address. loong64 watch
goes through the kernel's HW_WATCH regset (riscv64 reports the
kernel's interface as unsupported instead of failing obscurely).
- **`gasm debug` hung on the first faulting kernel.** Genuine
SIGSEGV/SIGBUS/SIGFPE/SIGILL stops were discarded as runtime noise
and the faulting instruction restarted forever; faults now surface as
reported stops. Conditional breakpoints with a false condition
resumed mid-instruction, `next` and `finish` evaluated traps with
stale registers and landed off instruction boundaries, and the
breakpoint restore covered one byte of the four-byte traps (arm64
silently skipped the instruction under it); the trap PCs follow the
kernel's reporting on every architecture. `regs` reports YMM from
the xstate (a struct-size overrun crashed FP register reads before),
V register halves print correctly on arm64, `unwatch` accepts the
architecture's slot range, `x <addr> -8` no longer crashes, break
conditions accept memory operands, and session scratch directories
are cleaned up.
- **The language server died on one malformed frame and corrupted
sources on rename.** A bad `Content-Length` or an unparsable JSON
body terminated the process instead of answering `-32700` and
continuing; rename and references covered the stripped name instead
of the full `·name` token, so renaming produced `·helper` minus its
last letter; documentHighlight never matched middle-dot symbols.
Positions are UTF-16 code units in both directions (astral characters
no longer shift columns) and responses always carry an explicit
`result` member.
- **The linter now recognises the `g` spelling of the goroutine
register.** `MOVD R0, g` clobbered R28 on arm64 (and the equivalents
on the other architectures) unflagged, and the numeric spellings the
linter did track are rejected by the toolchain there, so `g` was the
one spelling that escaped the audit. FUNCDATA and PCDATA literal
indices are validated against the ranges the runtime defines.
- **Usage errors exit 2 uniformly.** `audit-instructions` and
`scaffold` argument errors and an unknown `asm --format` exited 1 (or,
for `--format` without `-o`, exited 0 silently); the documented
exit-2 contract now holds, `asm -o` no longer prints the hex dump it
claimed to replace, and `verify --ground-truth` works for amd64
kernels on non-amd64 hosts instead of refusing with JIT advice.
## [0.33.0] - 2026-09-14 ## [0.33.0] - 2026-09-14