• v0.35.0
    Test / test (push) Successful in 2m26s
    Release / gates (push) Successful in 2m25s
    Release / build (amd64, linux) (push) Successful in 1m15s
    Release / build (arm64, linux) (push) Successful in 1m16s
    Release / build (loong64, linux) (push) Successful in 1m16s
    Release / build (riscv64, linux) (push) Successful in 1m16s
    Release / release (push) Successful in 34s
    Stable

    petrbalvin released this 2026-09-21 23:33:11 +00:00 | 0 commits to main since this release

    Added

    • 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
      the point of use, #undef and the #ifdef/#ifndef/#else/
      #endif family select branches, #include splices headers resolved
      through the source directory and the new repeatable -I flag, ;
      separates statements, and constant expressions left in operands
      ($(32-7), $~63, (index*4)(base)) fold at parse. Expansion
      happens only on the assembly path: gasm lint, gasm fmt and the
      language server keep reading the raw file.
    • Encoder coverage: the instruction families GOROOT's real code
      uses.
      The encoder now covers the
      instruction families GOROOT's real code uses that gasm lacked,
      byte-verified against go tool asm: on amd64 the carry ALU, the
      atomics (CMPXCHG, XADD, XCHG), AES-NI, SHA-1/256, PCLMULQDQ, CRC32,
      GFNI, ADX, BMI, the string primitives, the system set (CPUID, RDTSC,
      SYSCALL, fences, MXCSR) and the SSE/AVX/EVEX gaps; on arm64 the pair
      loads and stores (LDP/STP), acquire/release and LSE atomics, AES and
      SHA, the system operations, the bit ops and the NEON slice including
      structure loads and the literal-pool moves; on riscv64 the RV64A AMO
      family with aq/rl ordering, the Zbb pseudos with their RVC
      compressions, the FMA forms and the RVV slice with vsetvli/
      vsetivli; on loong64 the AM atomics with acquire/release forms, the
      LSX/LASX slice, the VMOVQ/XVMOVQ transfer family and FSEL.
      Also fixed on the way: arm64 CASD/CASW lacked an opcode bit, and
      riscv64 VSETVLI with an immediate length now canonicalises to
      vsetivli as the toolchain does.
    • Encoder coverage: quad-register AVX-512 and floating-point
      immediates.
      The encoder gains the
      quad-register AVX-512 families (4FMAPS, 4FNMADD, 4VNNIW, VP4DPWSSD,
      VP4DPWSSDS) with the register list riding the inverted V'VVVV field,
      floating-point immediates on the SSE scalar moves and arithmetic
      (the constant lands in a synthesised read-only pool, a positive zero
      collapses to XORPS exactly as the toolchain does), accept-and-ignore
      FUNCDATA and PCDATA, three-operand double shifts, static-symbol
      operands for the legacy SSE moves, and the pooled 64-bit immediate
      materialisation on riscv64. The parser carries bracketed register
      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.
    • Per-architecture reference pages. docs/asm/
      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/
      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
      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_<arch> and GOOS_<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.
    Downloads
  • v0.34.0
    Test / test (push) Successful in 2m11s
    Release / gates (push) Successful in 2m11s
    Release / build (amd64, linux) (push) Successful in 1m13s
    Release / build (arm64, linux) (push) Successful in 1m10s
    Release / build (loong64, linux) (push) Successful in 1m12s
    Release / build (riscv64, linux) (push) Successful in 1m33s
    Release / release (push) Successful in 58s
    Stable

    petrbalvin released this 2026-09-19 23:44:37 +00:00 | 54 commits to main since this release

    Added

    • Indirect JMP and CALL on all four architectures. JMP AX,
      CALL AX, JMP (BX) and the memory forms encode at byte parity with
      the toolchain (FF /2 and FF /4 on amd64); arm64 lowers JMP (R0) to
      BR and accepts the raw BR/BLR spellings; riscv64 lowers JMP (X5) to
      JALR; loong64 accepts the raw JIRL rd, rj, off spelling the Go
      assembler cannot express. A frameless amd64 function containing a
      CALL now receives the toolchain's forced base-pointer frame. The
      riscv64 and loong64 verify trampolines join their ground-truth lists,
      and a lint check for control flow through registers and memory extends
      to the new forms.
    • gasm asm -GOARCH and gasm diff -GOARCH. The target
      architecture can be named explicitly instead of inferred from the
      file-name suffix, which is how the suffix-less majority of GOROOT's
      .s files (cpu_x86.s, stub.s, ...) become assemblable.
    • gasm audit-instructions --corpus [dir]. Assembles every .s
      file under a directory (default GOROOT/src) with the gasm encoder
      only: suffixed files for their architecture, suffix-less files for
      all four, as a GOARCH build would. Reports the headline number (127
      of 627 GOROOT files, 20.3 %, assemble for every target architecture),
      the per-architecture pass rates and the most common failure reasons
      with a representative file each,
      which drive the encodability backlog by frequency.
    • The parser and the formatter are fuzzed. Two targets carry the
      guarantee: no input makes the parser panic, and every input yields a
      file the rest of the toolkit can work on; formatting twice equals
      formatting once, and clean input stays clean. They seed from the
      repository's own kernels, and just fuzz drives the mutation engine
      on demand.
    • Man pages. docs/man carries gasm(1) and a page for every command
      except version, which gasm(1) documents itself, written in roff:
      synopsis, description, every flag with its default, exit status,
      worked examples and cross-references.
      just install-man compresses them into ~/.local/share/man (MANDIR
      overrides) and just uninstall-man removes them. A test builds the
      binary and compares each page's flags and synopsis with its own -h
      output, so the pages cannot drift from the CLI.

    Changed

    • Go 1.27.1 required. The module declares go 1.27.1, so building
      from source needs that patch release or newer.
    • Canonical just recipes. just gates is the definition of done
      (build, fmt-check, vet, test, race). install now builds and copies
      the binary into ~/.local/bin (BINDIR overrides) instead of
      downloading module dependencies, and install-bin is gone. The test
      gate sweeps the logic packages (arch through verify; the ptrace-bound
      debug and the thin cmd/gasm sit outside the coverage profile), so
      the coverage floor is computed over the product code and the number is
      identical locally and in CI; the two excluded packages' own tests run
      in the gate and in the pipelines, outside the floor. fuzz requires
      its target package.
    • The reported version comes from the build. gasm --version
      prints the version the toolchain recorded: the tag on a tagged
      checkout, a pseudo-version naming the commit below one, +dirty on a
      dirty tree and (devel) outside version control. Nothing is
      injected with -ldflags -X any more.
    • CI realigned with the gate set. The push pipeline runs the gates
      minus race in one job, in the gates order, with a cached Go setup and
      the module as the version source; a superseded run of the same branch
      is cancelled instead of queueing; every go test runs under a
      ten-minute bound that matches its job's; the race detector moved to a
      hand-dispatched workflow and runs in the local gate before a tag is
      cut, never on a push or a tag; the release builds without injection and
      its smoke test requires the recorded tag and rejects +dirty.
    • The documents follow the standard set. docs/ARCHITECTURE.md is
      organised as Overview, Packages, Data flow, State and lifetime and
      Dependencies, and carries a sequence diagram of the assembly path;
      docs/DEVELOPMENT.md lists every recipe in one table and documents the
      coverage floor, the CI and the release flow; docs/CLI.md gives the
      synopsis, the commands, every flag with its default, the exit codes and
      worked examples; CONTRIBUTING.md carries the Contributor terms and
      states the commit trailer form, the one-logical-change rule and the
      licence header rule; SECURITY.md states how a vulnerability is
      reported and what to expect. 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. A Validation status section
      states what has been executed where: amd64 on real hardware, the other
      three architectures under qemu-user emulation, the encoding parity on
      the host for all four, and the debugger's ptrace path on amd64 only.

    Fixed

    • riscv64 JALR silently jumped to the wrong register. The trampoline
      form JALR X0, 0(X5) read the memory operand's base as the destination,
      encoding a jump to X0 with no diagnostic; the destination is the first
      operand. The leaf detection shared the confusion, so affected functions
      also grew a bogus prologue. JALR X0, 0(X1) as written in the verify
      trampoline was mis-encoded since its introduction.
    • DATA lines demanded their GLOBL first. collectData processed the
      declarations in file order, but the Plan 9 convention puts every DATA
      line before its symbol's GLOBL; correctly ordered files (most of
      GOROOT's) failed with "no matching GLOBL". Two passes: symbols are
      registered before initialisers are applied.
    • The formatter lost idempotency on degenerate lines. Illegal tokens
      survived into the output, a label sharing its line with a
      non-instruction split into a line the parser rejects, stray-operand
      lines entered the alignment width computation, and rendered / *,
      > > sequences re-lexed as comments and shifts. The label, width and
      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.
    • The class-2 stack guard branched four bytes past its target. When
      the underflow branch relaxed to its 32-bit form, its displacement was
      still computed as if the branch were two bytes long, so it landed
      inside the morestack CALL instead of the compare that decides it.
      The long form is reachable once a large frame carries a body of roughly
      a hundred bytes.
    • 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.
    • arm64 store-exclusive instructions read their operands in the
      toolchain's order.
      STXR treated the first register as the status
      register where go tool asm reads it as the data register, so the
      same source assembled to different code in the two assemblers; the
      pair forms (STXP, LDXP and their acquire/release variants) are
      accepted now, in the toolchain spelling.
    • Large arm64 frames matched the toolchain's sequences. A frame
      beyond the immediate range that is not a movcon constant (roughly
      64 KiB and up) made gasm verify report a false mismatch: the
      toolchain splits the prologue subtraction into two 12-bit immediates
      and materialises the non-leaf epilogue addition through the temporary
      register; gasm emits the same sequences and the spadj boundaries
      follow the real word counts.
    • The width spellings GOROOT uses assemble. MOVLQZX (four uses in
      runtime/asm_amd64.s), MOVBQSX, MOVWQSX, MOVBLSX, MOVBWSX,
      MOVBWZX and PMOVMSKB (the bytealg kernels) encode byte-identically
      with go tool asm, and the linter reports them encodable; a
      MOVLQZX is the plain 32-bit move, exactly as the toolchain lowers
      it.
    • verify --ground-truth no longer reports a mismatch for functions
      whose size is not a multiple of 16.
      The toolchain pads text symbols
      to 16-byte boundaries; the comparison now checks the padding is zero
      instead of comparing it, the same rule the test suite applies.
    • riscv64 accepts the g spelling of the goroutine register, like
      the other architectures, and the abi kernels use it; every verify
      kernel is now ground-truth checkable (the numeric X27 spelling the
      kernels used is one go tool asm rejects).
    • Two more spellings GOROOT uses now assemble. riscv64 FCLASSD
      (classify a float64 into an integer mask) is encodable, and a
      displacement written as a product (0*8(X5), the toolchain's own
      spelling in several kernels) parses instead of being rejected.
    • loong64 JIT execution enabled. The loong64 trampoline is now
      validated end to end under qemu-user emulation (plain and ABI-checked
      calls, goroutine-clobber detection), so gasm verify runs the JIT
      checks on loong64 hosts instead of forcing every loong64 kernel down
      the ground-truth path. The arm64 and riscv64 trampolines carry the
      same validation; the arm64 ABI test now seeds its kernel arguments
      (a zeroed block made the passthrough check meaningless), and the
      loong64 basic kernel's branch maze terminates on every path so the
      smoke sweep cannot spin on leftover register values.
    Downloads
  • v0.33.0
    Release / build (amd64, linux) (push) Successful in 49s
    Release / build (arm64, linux) (push) Successful in 43s
    Release / build (loong64, linux) (push) Successful in 46s
    Release / build (riscv64, linux) (push) Successful in 45s
    Test / vet (push) Successful in 47s
    Release / release (push) Successful in 18s
    Test / test (push) Successful in 2m39s
    Test / build (push) Successful in 43s
    Stable

    petrbalvin released this 2026-09-14 21:36:25 +00:00 | 110 commits to main since this release

    Added

    • Stack-split guards in gasm asm. Every framed function now gets
      the morestack prologue check and the trailing morestack block
      (CALL runtime.morestack_noctxt), byte-identical to the toolchain's
      stacksplit output on all four architectures: the small, medium and
      big frame classes, auto-NOSPLIT leaves, the materialised constants of
      large frames (arm64 R27, riscv64 X31, loong64 R30) and the
      arm64 extrasize rule. Assembled objects are therefore linkable for
      split functions, not only NOSPLIT leaves.
    • gasm dis. Standalone disassembly through golang.org/x/arch:
      a .s file is assembled and listed per TEXT function with local
      labels at their real offsets, or raw bytes from a file or stdin are
      disassembled linearly (-a selects the architecture). The debugger
      shares the same decoder instead of carrying its own.
    • gasm fmt -l and -d. Check mode lists files whose formatting
      differs; diff mode prints a unified diff from the project's own
      LCS-based differ, with GNU header semantics.
    • LSP cross-file navigation. Go-to-definition and find references
      fall back from local labels to the TEXT functions of every open
      document, and rename follows the same cross-file matching.
    • Large frame offsets on riscv64 and arm64. Frame-relative loads
      and stores beyond the signed 12-bit immediate range materialise the
      address through the toolchain temp register (riscv64 X31, arm64
      R27) instead of silently truncating the offset (riscv64) or
      rejecting the instruction (arm64); arm64 frame sizes now add the
      toolchain's extrasize exactly (+8 when the frame leaves an alignment
      gap, +16 when it is already aligned).
    • Tail calls JMP sym(SB) on all four architectures (amd64 E9,
      arm64 B, riscv64 JAL X0, loong64 B) with the call relocation.
    • Live oracle-parity tests. Kernel files covering every guard
      class, large-offset pattern and tail call are assembled by gasm and
      by the installed go tool asm and compared byte-for-byte on all four
      architectures, alongside the existing pinned-byte tests.

    Fixed

    • The v0.32.0 review findings: the parser rejects malformed TEXT
      frames and parses signed frame sizes; amd64 frame adjustments above
      127 bytes encode with imm32; arm64 and loong64 relocation encodings
      match the toolchain; the linter guards unnamed TEXT directives and
      refreshes its textflag table; the LSP recovers from handler panics
      and decodes client URIs; watchpoint slot state moved into the debug
      session; dead verify code removed; em and en dashes replaced across
      sources.
    • gasm asm --format goobj: internal calls to TEXT symbols of the
      same file resolve on every architecture (the reference check accepted
      only the amd64 call kind).
    • gasm asm --format elf on loong64: branch relocations now map to
      R_LARCH_B26 instead of falling into R_LARCH_PCALA_HI20.
    • arm64 large-prologue ADD/SUB use the extended-register encoding
      the toolchain picks, and the morestack block saves the link register
      with the toolchain's OR form on loong64.
    Downloads
  • v0.32.0
    Release / build (amd64, linux) (push) Successful in 43s
    Release / build (arm64, linux) (push) Successful in 42s
    Release / build (loong64, linux) (push) Successful in 43s
    Release / build (riscv64, linux) (push) Successful in 43s
    Test / vet (push) Successful in 49s
    Release / release (push) Successful in 18s
    Test / test (push) Successful in 2m40s
    Test / build (push) Successful in 42s
    Stable

    petrbalvin released this 2026-08-31 11:10:53 +00:00 | 130 commits to main since this release

    Added

    • Multi-architecture debugger. gasm debug carries
      per-architecture ptrace register access, disassemblers
      (golang.org/x/arch), register display, FP register views and
      stop-info handlers for arm64, riscv64 and loong64, and the REPL is
      arch-neutral. Sessions are runtime-validated on amd64; the other
      hosts execute through the now-working JIT trampolines, but their
      ptrace loops have not seen hardware yet.
    • Headless debugging. gasm debug --script runs REPL commands from a
      file (or stdin) and exits; --timeout kills the debuggee when a run
      hangs, with the watchdog armed before the ptrace attach.
    • Conditional breakpoints. break <label> if <reg> <op> <val> now also
      compares two registers (break loop if RAX > RBX), not only a register
      against an immediate.
    • Instruction-level coverage. gasm debug --cover now sets a
      breakpoint on every instruction (walked by disassembly length), counts
      the hits per instruction and reports the executed instructions with
      their hit counts, with the label coverage derived from the same run.
      Expect the run to slow to ptrace speed.
    • FP register display on riscv64 and loong64. The debugger regs
      command shows the 32 FP registers plus fcsr (and fcc on loong64) via
      PTRACE_GETREGSET.
    • JIT execution trampolines. verify.Call now works on all four
      architectures via hand-written assembly trampolines
      (trampoline_{arm64,riscv64,loong64}.s) that save the Go stack, switch
      to a prepared stack, and branch to the JIT function.
    • ABI checks on arm64, riscv64 and loong64. gasm verify -abi and
      the ABI half of -fuzz now cover the non-amd64 architectures via
      per-architecture checked trampolines: sentinels planted in the
      registers the Go ABI fixes across calls (arm64 R29/R28, riscv64
      X27, loong64 R22; amd64 keeps BP/R14) are verified on return
      and the saved registers restored before Go code resumes, with the
      below-SP canary on every architecture. loong64 kernels are verified
      through the toolchain-comparison path only, pending hardware
      validation of their trampoline. verify.Load assembles each file
      with the encoder its name suffix calls for. The ABIReport fields
      are the architecture-neutral FPClobbered and GClobbered.
    • Hardware watchpoints on all architectures. arm64 uses DBGWVR/DBGWCR
      via PTRACE_SETREGSET with NT_ARM_HW_BREAK; riscv64 and loong64 use
      PTRACE_POKEUSER to access trigger/debug registers.
    • Cross-package GOOBJ resolution on arm64 and loong64.
      AssembleFileARM64 and AssembleFileLOONG64 mark external
      relocations and populate img.Externals, so GOOBJ output from those
      architectures resolves cross-package symbols like amd64 and riscv64
      already do.
    • gasm verify --args. Scalar arguments (name=value, decimal or
      0x hex) can now be supplied to a --call invocation alongside --buf
      buffers, closing the gap where only buffers could be supplied.
    • Fuzz corpus save and replay. gasm verify --fuzz --save-corpus dir
      records every input that crashes or mismatches as replayable JSON (buffer
      contents and scalars, not raw pointers), and gasm verify --replay dir
      re-runs the saved entries against the kernel in isolated child processes,
      reporting whether each one reproduces.
    • gasm audit-instructions. Black-box diff of a gasm encoder
      against the installed go tool asm, for amd64, arm64, riscv64 and
      loong64 (gasm audit-instructions <arch>): superset encodings
      (gasm-only, shippable via gasm asm --format goobj),
      known-but-unencodable names (the backlog) and go-only names
      (feature gaps).
    • gasm scaffold differential. Prints a differential test skeleton
      for every // func signature in a kernel file: random seed states, the
      kernel call and a portable reference (<name>Portable), compared
      byte-for-byte.
    • LSP: find references (textDocument/references).
    • LSP: rename symbol (textDocument/rename).
    • LSP: document formatting (textDocument/formatting) using the
      format package for canonical gofmt-style output.
    • LSP: inlay hints (textDocument/inlayHint): frame size hints after
      the TEXT directive's argument area.
    • LSP: workspace symbol search (workspace/symbol): substring search
      over the TEXT functions and GLOBL/DATA symbols of every open document.
    • LSP: code actions. Quick fixes for missing-ret (insert the RET)
      and unused-label (remove the label) diagnostics.
    • LSP: signature help (textDocument/signatureHelp): the callee's
      // func signature while the cursor is on a CALL.
    • LSP: document highlights: every reference to the function or label
      under the cursor is highlighted.
    • LSP: pull diagnostics (textDocument/diagnostic), #include
      document links
      (resolved against the document directory, then
      $GOROOT/pkg/include, so textflag.h opens) and folding ranges
      (one collapsible region per TEXT function body).
    • Lint: unused-label rule. Flags labels that are defined but never
      referenced by any jump (Hint severity).
    • Lint: invalid-textflag rule. Flags TEXT/GLOBL flags not in the
      known set from textflag.h (Warning severity).
    • Lint: stack-imbalance rule. Tracks SP changes and flags if the
      net delta at RET does not match the declared frame size.
    • Lint: register-width-mismatch rule. Flags amd64 operands whose
      register width does not match the width the mnemonic suffix prescribes
      (for example a 32-bit register in a MOVQ).
    • Lint: abi0-register-args rule. Flags kernels whose // func
      parameters are never read from the FP frame (for example arguments read
      from registers instead), which pass every test today and break on a
      toolchain upgrade. Shipped with the Go assembler's operand-width model.
    • Lint: nonportable-register-name rule. Flags the RAX/EAX-style
      register aliases gasm accepts but go tool asm rejects, so files using
      them only link through the gasm GOOBJ path.
    • Lint: unencodable-instruction rule. Flags mnemonics the
      architecture table knows but the encoder cannot yet emit, at edit time
      instead of at assembly time.
    • Lint: reserved-register-write rule. Flags writes to arm64 R18,
      the platform-reserved register the ABI checks cannot observe at runtime
      and the Go assembler cannot even spell. Reads and macro-using files
      are exempt.
    • DWARF5 debug sections in ELF output. All four ELF emitters now emit
      .debug_abbrev, .debug_info, .debug_line, and .debug_line_str
      sections, enabling addr2line and GDB/LLDB source-level debugging, and
      the amd64 emitter adds a .debug_frame CFI section for stack unwinding.
    • amd64: legacy SSE and conversion coverage. The encoder now handles
      the legacy (non-VEX) SSE packed binaries and immediate shuffles, the
      legacy SSE integer instructions and BSWAP, the scalar and packed
      double conversions (CVTSS2SD, CVTSD2SS, CVTPS2PD, CVTPD2PS) and
      the prefetch hints.
    • amd64: VPCMP and the full opmask set. The EVEX compare with an
      opmask destination and the remaining opmask-register instructions are
      encoded, byte for byte against the Go assembler.

    Changed

    • Parallel verify sweeps. The -smoke and -abi per-function checks
      now run in parallel instead of sequentially.

    Fixed

    • Reliable ptrace sessions. The debugger no longer mistakes runtime
      signal-delivery-stops (a Go tracee reports SIGURG preemption to the
      tracer) for its launch barrier, runs every ptrace request on the thread
      that forked the debuggee (requests from another thread fail with
      ESRCH), prefers the debuggee's reported code base over an RWX scan, and
      single-steps over a hit breakpoint so resuming cannot re-trap on the
      same instruction. A ptrace integration test
      (debug/ptrace_integration_test.go) drives a real session end to end.
    • arm64 BL relocation. BL sym(SB) now records a RelArm64Branch
      relocation instead of emitting a bare instruction with no relocation.
    • GOOBJ R_ADDRARM64 constant. Corrected from 9 (R_CALLARM64) to 3
      (R_ADDRARM64).
    • Subprocess-isolated smoke and ABI sweeps. A function that faults
      during the -smoke or -abi sweep is reported without killing the
      parent; each sweep runs in a child process.
    • BSF, BSR and POPCNT encodings. Corrected to the bytes
      go tool asm emits.
    • MOVQ immediates. Compressed to the toolchain's imm32 forms.
    • Frame adjustments of 128 to 255 bytes. Now use the imm8 ADDQ
      stack adjustment.
    • amd64 encoding parity. A broad pass aligned the remaining encoder
      outputs and operand strictness with go tool asm.
    • asm help text. Updated to list arm64 as a supported architecture.
    Downloads
  • v0.31.1
    Test / vet (push) Successful in 47s
    Release / build (amd64, linux) (push) Successful in 55s
    Release / build (arm64, linux) (push) Successful in 51s
    Release / build (loong64, linux) (push) Successful in 46s
    Release / build (riscv64, linux) (push) Successful in 42s
    Test / test (push) Successful in 2m29s
    Release / release (push) Successful in 19s
    Test / build (push) Successful in 42s
    Stable

    petrbalvin released this 2026-08-20 20:35:11 +00:00 | 195 commits to main since this release

    Fixed

    • Version stamp. The v0.31.0 release binary reported itself as 0.30.0
      because the version variables in justfile and cmd/gasm/main.go were not
      bumped during the release commit.
    Downloads
  • v0.31.0
    Release / build (amd64, linux) (push) Successful in 42s
    Release / build (arm64, linux) (push) Successful in 40s
    Release / build (loong64, linux) (push) Successful in 42s
    Release / build (riscv64, linux) (push) Successful in 45s
    Release / release (push) Successful in 18s
    Stable

    petrbalvin released this 2026-08-20 14:24:44 +00:00 | 197 commits to main since this release

    The arm64 encoder (Phase 5 — complete) ships with ELF64 and GOOBJ emission,
    verified byte-for-byte against GOARCH=arm64 go tool asm and linked into a
    real go build. The encoder covers the full integer instruction set, FP
    arithmetic, conditional select, CRC32, and the MOV pseudo-instruction with
    bitmask immediate encoding. The project now requires Go 1.27.

    Added

    • arm64 encoder (Phase 5 — complete). gasm asm can now assemble _arm64.s
      files: the AArch64 integer instruction set with the MOV pseudo-instruction and
      its immediate-constant expansions (MOVZ/MOVN/MOVK for wide immediates, ORR with
      logical bitmask encoding for values like $1), data-processing (shifted
      register and immediate forms), load/store (scaled unsigned and unscaled9-bit
      immediate), conditional and unconditional branches, FP/SP frame mapping,
      SB/global symbol references (ADRP+ADD pairs with R_ADDRARM64 relocations),
      jump chain folding, and ELF64 emission (gasm asm --format elf). Ground-truth
      verification against GOARCH=arm64 go tool asm matches byte-for-byte. Phase 5
      (the other architectures — RISC-V, LoongArch, arm64) is now complete.

    Changed

    • Go 1.27 required. The project now requires Go 1.27 (toolchain go1.27.0).
      The R_DWTXTADDR_U4 relocation type is detected at runtime for backward
      compatibility.
    Downloads
  • v0.30.0
    Test / vet (push) Successful in 43s
    Test / test (push) Failing after 1m55s
    Test / build (push) Skipped
    Release / build (amd64, linux) (push) Successful in 40s
    Release / build (arm64, linux) (push) Successful in 37s
    Release / build (loong64, linux) (push) Successful in 38s
    Release / build (riscv64, linux) (push) Successful in 58s
    Release / release (push) Successful in 22s
    Stable

    petrbalvin released this 2026-08-13 16:34:20 +00:00 | 206 commits to main since this release

    The LoongArch encoder (Phase 5) ships with ELF64 and GOOBJ emission, verified
    byte-for-byte against GOARCH=loong64 go tool asm and linked into a real
    go build; the shared GOOBJ emitter now writes the per-function DWARF symbols
    the linker's DWARF pass reads. The RISC-V encoder reaches byte-for-byte parity
    with go tool asm: the frame model, operand ordering, RVC compression,
    large-immediate and MOV $imm materialisation, branch/jump encodings, and
    CALL sym(SB) (now a JAL with an R_RISCV_JAL relocation). The debugger
    tracks four hardware watchpoint slots, and the toolkit is Linux-only.

    Added

    • LoongArch encoder (Phase 5). gasm asm can now assemble _loong64.s
      files: the full LoongArch64 instruction set with the dual-form arithmetic
      mnemonics, the 16/21-bit branch families, the MOV pseudo-instruction and
      its immediate-constant expansions, FP/SP frame mapping, SB/global symbol
      references (pcalau12i pairs) and ELF64 emission
      (gasm asm --format elf). Ground-truth verification against
      GOARCH=loong64 go tool asm matches byte-for-byte; GOOBJ emission
      (gasm asm --format goobj) is proven end-to-end by linking the object
      into a cross-compiled go build.
    • GOOBJ DWARF symbols. The GOOBJ emitters now write the per-function
      DWARF symbols the linker requires (the subprogram DIE and the .debug_line
      program, byte-identical to cmd/asm's), and the pc-value table deltas are
      in the architecture's MinLC units as the runtime expects — the amd64 link
      test now genuinely substitutes the gasm object, and the amd64/loong64
      end-to-end GOOBJ link tests pass.
    • RISC-V GOOBJ emission via the shared emitter. RISC-V GOOBJ output is
      now written by the same shared emitter as amd64 and LoongArch, modelling
      each AUIPC + second-instruction pair as a single R_RISCV_PCREL_ITYPE/STYPE
      relocation (the layout cmd/asm writes, not the ELF HI20/LO12 pair), so the
      object links into a cross-compiled go build for GOARCH=riscv64. An
      end-to-end link test substitutes the gasm object and reads the symbol back
      with go tool nm; the rewrite also corrects the relocation after field.

    Fixed

    • RISC-V frame model and RVC encodings. The riscv64 frame layout now
      matches go tool asm: the prologue/epilogue save and restore the link
      register (LR) instead of S0, with the correct autosize (locals + 8) and the
      RVC-compressed prologue/epilogue instructions; RET emits the uncompressed
      JALR X0, 0(X1) the toolchain writes; the C.ADDI/C.LI/C.LUI/C.ADDIW
      opcode bit and the C.ADD CR-type encoding are fixed; and the LR/TMP
      register aliases now resolve to X1 and X31. The pcsp/pcfile/pcline tables
      are populated from the recorded stack-adjustment and source-line data, and
      a byte-exact ground-truth test compares framed and leaf functions against
      GOARCH=riscv64 go tool asm.
    • RISC-V operand ordering and RVC compression. R-type instructions now
      take rs2, rs1, rd and I-type arithmetic instructions take imm12, rs1, rd, matching the Go assembler's documented operand order (previously both
      were reversed, so non-commutative R-type instructions such as SUB encoded
      the wrong operation). The two-operand ternary forms (ADD rs2, rd,
      ADDI $imm, rd, SLLI $shamt, rd) are now accepted. RVC compression is
      completed for C.ADDI16SP, C.SLLI, C.SRLI, C.SRAI, C.ANDI,
      C.NOP, C.EBREAK, C.MV (from ADDI/ADD) and the commutative
      AND/OR/XOR forms; the byte-exact ground-truth test now covers these.
    • RISC-V compressed loads/stores and word arithmetic. RVC compression
      now also covers the register-relative C.LW/C.SW/C.LD/C.SD/
      C.FLD/C.FSD forms (in addition to the stack-relative C.LWSP/C.SWSP/
      C.LDSP/C.SDSP), plus C.ADDI4SPN, C.ADDW and C.SUBW. The
      byte-exact ground-truth test exercises these against GOARCH=riscv64 go tool asm.
    • RISC-V large-immediate materialisation. ADDI/ANDI/ORI/XORI
      with a 32-bit immediate that does not fit 12 bits now expand exactly as
      cmd/asm: two ADDIs for the small ADDI split range, and
      LUI+ADDIW+<op> otherwise, with the LUI and ADDIW compressed to
      C.LUI/C.ADDIW when their immediate fits six signed bits. The
      byte-exact ground-truth test covers positive, negative, and out-of-range
      immediates against GOARCH=riscv64 go tool asm.
    • RISC-V MOV $imm, rd materialisation. The immediate-loading
      pseudo-instruction now uses the toolchain's Split32BitImmediate split
      (previously it rounded the upper 20 bits, producing wrong results for
      negative and bit-11-set immediates) and compresses the emitted
      ADDI/LUI/ADDIW to C.LI/C.LUI/C.ADDIW when their immediate
      fits six signed bits. A byte-exact ground-truth test covers zero, small,
      negative, and 32-bit immediates against GOARCH=riscv64 go tool asm.
    • RISC-V branch/jump compression. JMP/JAL were being compressed to
      C.J and BEQ/BNE (with X0) to C.BEQZ/C.BNEZ, but go tool asm
      never emits these compressed forms. They now emit the 32-bit JAL and
      branch encodings the toolchain writes; the dead C.J/C.BEQZ/C.BNEZ
      encoders were removed, and the C.LUI direct-instruction compression now
      uses the correct six-bit signed range. A byte-exact ground-truth test
      covers the branch family and jumps against GOARCH=riscv64 go tool asm.
    • RISC-V CALL sym(SB). The call pseudo-instruction now emits the
      toolchain's JAL X1, sym(SB) with a single R_RISCV_JAL relocation
      (previously it emitted an AUIPC+JALR pair against a local branch
      label, a form go tool asm rejects). The GOOBJ and ELF emitters now map
      that relocation (Go objabi 59 / ELF R_RISCV_JAL 17, a 4-byte field), and
      relocation offsets are recorded relative to the function start (including
      the prologue). A byte-exact ground-truth test covers a call against
      GOARCH=riscv64 go tool asm.
    • Debugger watchpoint slots. gasm debug's watch command always used
      hardware watchpoint slot 0, so a second watch call silently overwrote
      the first. Watchpoint slots are now tracked in the Session (DR0–DR3);
      watch picks the first free slot and reports an error if all four are in
      use, and unwatch <slot> clears one (no argument clears all).

    Changed

    • Linux only. The toolkit, its CI and the released binaries are now
      Linux-only; cross-compiled to linux/{amd64,arm64,riscv64,loong64}.
    • Phase 4 closed. README's "Remaining" list for the debugger is gone;
      disassembly at PC, memory-write, watchpoints, and source-line mapping are
      all shipped.
    Downloads
  • v0.29.0
    Test / vet (push) Successful in 43s
    Release / build (amd64, linux) (push) Successful in 41s
    Release / build (arm64, linux) (push) Successful in 38s
    Release / build (loong64, linux) (push) Successful in 39s
    Release / build (riscv64, linux) (push) Successful in 38s
    Test / test (push) Successful in 1m58s
    Release / release (push) Successful in 18s
    Test / build (push) Successful in 37s
    Stable

    petrbalvin released this 2026-08-07 19:34:09 +00:00 | 221 commits to main since this release

    RISC-V GOOBJ emission, YMM vector register display, named buffer allocation
    in the debugger, two new CLI commands (diff, profile), go-to-definition in
    the LSP, combined ABI+fuzz verification, and did-you-mean label suggestions.
    A --map flag for diff and --call/--buf flags for verify extend the
    new CLI commands. A signature-parser fix corrects grouped Go parameters.

    Added

    • RISC-V GOOBJ emission — gasm asm --format goobj for RISC-V produces
      linkable Go objects with funcdata, pc-value tables, and RISC-V relocation
      types (same format as amd64 GOOBJ, with the RISC-V architecture marker).
    • gasm diff — compare the machine code of two assembly files byte-for-byte;
      shows which functions differ and the first few differing bytes.
    • gasm profile — show the basic-block structure of each function: labels,
      offsets, frame size, and NOSPLIT flag.
    • LSP go-to-definition — textDocument/definition navigates from a label
      reference to its definition.
    • did-you-mean — when the RISC-V assembler encounters an undefined label, it
      suggests the closest existing label using Levenshtein distance.
    • YMM vector register display — regs in the debugger now shows YMM
      registers via PTRACE_GETFPREGS (falls back to XMM when XSAVE is unavailable).
    • Named buffer allocation — gasm debug --buf name:size:pattern allocates
      buffers in the debuggee filled with zero, ones, seq, or a hex pattern;
      buffer pointers are placed into the argument block at the matching positions.
    • Crash input storage — FuzzResult.CrashInput stores the input that caused
      a crash or mismatch for reproducibility.
    • ABI + fuzz combined — gasm verify --fuzz now runs ABI checks (sentinel
      registers, canary, stack bounds) alongside differential fuzz testing.
    • gasm diff --map — compare functions whose names differ between files
      (e.g. --map wideCopyAVX2=wideCopyAVX512 pairs two variants regardless
      of suffix). Unmapped functions fall back to the original name match.
    • gasm verify --call — invoke a single function with user-supplied buffers
      (--buf name:size:pattern) instead of the smoke/abi/fuzz sweeps. Patterns:
      zero, ones, seq, or a hex blob. Useful for partial functions (e.g.
      decoders) that crash on random input but should succeed on valid data.
      The arg block is printed before and after the call, showing return values.
    • gasm verify --ground-truth now documented in --help (was already a flag,
      just missing from the help text).

    Fixed

    • Signature parser — grouped Go parameters like dst, src []byte are now
      parsed correctly (both get type []byte). Previously the first name was
      treated as its own type (dst with size 8), causing wrong ABI0 arg-block
      layout in both verify --call and the fuzzer.
    • Flaky JIT tests — runtime.KeepAlive guards and package-level buffers
      prevent GC from collecting heap objects whose addresses were passed to JIT
      code via unsafe.Pointer; all verify tests pass 100/100 under -race.

    Cleaned up

    • Removed external kernel test dependencies — the verify test suite no
      longer references production kernels from the separate go-libraries project.
      The remaining test suite uses only testdata/verify/*.s kernels, which are
      part of this repository. Coverage is identical locally and in CI (80.3 %).

    Verified

    • gasm diff detects byte-level differences; --map pairs differently-named
      functions for comparison.
    • gasm verify --call invokes functions with user-supplied buffers; the arg
      block is printed before and after the call, showing return values.
    • LSP go-to-definition resolves labels across functions and files.
    Downloads
  • v0.28.0
    Test / vet (push) Successful in 44s
    Release / build (amd64, linux) (push) Successful in 39s
    Release / build (arm64, linux) (push) Successful in 38s
    Release / build (loong64, linux) (push) Successful in 39s
    Release / build (riscv64, linux) (push) Successful in 39s
    Test / test (push) Successful in 1m58s
    Release / release (push) Successful in 18s
    Test / build (push) Successful in 37s
    Stable

    petrbalvin released this 2026-08-03 17:42:05 +00:00 | 240 commits to main since this release

    RISC-V encoder: full RV64IMAFDC instruction set with RVC compression, MOV
    pseudo-instruction, SB/global symbol references, ELF64 object emission, and
    ground-truth verification against GOARCH=riscv64 go tool asm.

    Added

    • RISC-V encoder — RV64I, RV64M, RV64A, RV64F/D, FMA, CSR, JALR.
    • MOV pseudo-instruction — load, store, reg-to-reg, immediate, frame mapping.
    • RVC compression — 22 compressed instruction types (C.LDSP, C.SDSP, C.FLDSP,
      C.FSDSP, C.ADDI, C.LI, C.LUI, C.ADDIW, C.MV, C.ADD, C.SUB, C.XOR, C.OR, C.AND,
      C.SLLI, C.SRLI, C.SRAI, C.ANDI, C.BEQZ, C.BNEZ, C.J, C.JR).
    • SB/global symbols — MOV $sym(SB), MOV sym(SB), MOV rd, sym(SB)
      encoded as AUIPC pairs with R_RISCV_PCREL_HI20/LO12 relocations.
    • GLOBL/DATA — data section layout in AssembleFileRISCV.
    • ELF64 emission — gasm asm --format elf produces EM_RISCV objects
      (.text, .data, .symtab, .rela.text).
    • gasm verify --ground-truth — byte-exact comparison against
      GOARCH=riscv64 go tool asm.
    • gasm verify --profile — function layout listing for RISC-V.
    • CALL — AUIPC + JALR pair encoding.

    Fixed

    • Parser: bare-number offset before (SP) no longer misidentified as pseudo.
    • MOV: MOV $sym(FP/SP), rd now returns an explicit error instead of silent fallback.
    • RVC: C.LDSP/C.SDSP/FLDSP/FSDSP immediate encoding now matches Go toolchain
      (bit-interleaved format).

    Verified

    • 118 RISC-V tests, asm coverage 83.3%.
    • Ground-truth: C.LDSP, C.SDSP, C.FLDSP, C.FSDSP byte-exact vs Go toolchain.
    Downloads
  • v0.27.0 Stable

    petrbalvin released this 2026-08-02 21:11:30 +00:00 | 252 commits to main since this release

    Subprocess isolation for --fuzz: each function is fuzzed in its own child
    process, so a partial function (decoder) that faults on random garbage is
    reported as "CRASH (partial function, use --ground-truth)" without killing
    the parent. CRASH is informational (exit 0); only MISMATCH is an error.

    Fixed

    • gasm verify --fuzz no longer crashes the process on partial functions.
    Downloads