Skip to content

plan9asm: complete ARM64 scalar instruction families - #35

Merged
xushiwei merged 11 commits into
xgo-dev:mainfrom
cpunion:codex/arm64-go-assembler-families-20260910
Sep 13, 2026
Merged

plan9asm: complete ARM64 scalar instruction families#35
xushiwei merged 11 commits into
xgo-dev:mainfrom
cpunion:codex/arm64-go-assembler-families-20260910

Conversation

@cpunion

@cpunion cpunion commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Complete the ARM64 scalar instruction and operand families exposed by the Go assembler corpus around xgo-dev/llgo#2552, including the bitfield aliases, conditional-select aliases, 32/64-bit shifts and reversals, flag-setting forms, signed and unsigned narrow moves with shifted or extended indexed addressing, and FMOVQ memory forms. The implementation is defined by Go's ARM64 encoder tables and official positive assembler tests, not by a third-party package.

Add an executable semantic oracle that runs the same ARM64 assembly through the native Go assembler and through plan9asm-generated LLVM, covering 59 normalized forms and checking 72 exact results. The Go 1.20 through Go 1.27 corpus gate now requires every one of the 54 completed official opcodes to be encoder-defined, observed in official positive testdata, and free of unsupported observed operand forms.

Add the complete pinned golang.org/x/arch/arm64/arm64asm Plan 9 decoder corpus as a larger supplemental source. Because decoder output can include reserved encodings or forms rejected by the Go assembler, the completed-family subset is filtered through native go tool asm before plan9asm support is required.

Use github.com/klauspost/compress v1.20.0 only as the final external regression consumer: all 14 amd64/arm64 assembly files are translated and LLVM-compiled after the authoritative gates pass. With latest llgo main and this branch connected by go.work, LLGO_PLAN9ASM_PKGS=all llgo test github.com/klauspost/compress/huff0 and github.com/klauspost/compress/zstd/internal/xxhash both pass, and a huff0 compression/decompression round trip matches native Go.

Extend the durable coverage gate to all five independent GOARCH targets used by LLGo's Plan 9 path: 386, amd64, arm, arm64, and wasm. The standard-library gate now expands Linux and wasm targets across all settings supported by each selected Go release, including GO386 sse2/softfloat, GOAMD64 v1-v4, GOARM 5/6/7 and available float ABIs, GOARM64 v8.0-v9.5 plus lse/crypto boundaries, and all GOWASM satconv/signext combinations for js/wasm and wasip1/wasm. WebAssembly uses Go's official opcode namespace plus concrete GOROOT assembly because its backend has no x86/ARM-style operand-class encoder table; those opcode-only rows are explicitly reported rather than presented as encoder forms. Closes #36.

Addresses xgo-dev/llgo#2552.

Validation

  • go test ./...
  • go test -race ./...
  • go test -coverprofile=... ./... (87.8% aggregate statement coverage)
  • scripts/check-go-asm-coverage.sh under the latest patch releases of Go 1.20 through Go 1.27, including each version's official wasm namespace and GOROOT corpus
  • scripts/check-arm64-plan9-corpus.sh (1,108 normalized forms; all 489 native-Go-accepted cases from the completed families lower successfully)
  • scripts/check-klauspost-compress.sh (all 14 assembly files translate and LLVM-compile)
  • scripts/check-stdlib-corpus.sh (44 Go 1.27 target configurations; all scan, translate, and LLVM-compile with zero unsupported forms and zero parse failures; 352 seconds locally)
  • Go 1.20 ARM 5/6/7 and implicit ARM64 v8.0 matrix, plus Go 1.22 ARM float-ABI matrix, verified with their own GOROOT binaries
  • latest llgo main 2350db1cc3ea4e47446b6de54e557f02ce78532d via go.work, with LLGO_PLAN9ASM_PKGS=all: huff0 test, xxhash test, and huff0 round-trip program pass

@codecov

codecov Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.35484% with 99 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
amd64_lower_vec.go 72.41% 48 Missing ⚠️
amd64_lower_arith.go 75.00% 14 Missing ⚠️
arm64_lower_arith.go 96.12% 11 Missing ⚠️
arm64_eval.go 93.33% 6 Missing ⚠️
arm64_lower_vec.go 88.46% 6 Missing ⚠️
arm64_ctx.go 82.14% 5 Missing ⚠️
types.go 94.59% 4 Missing ⚠️
arm64_lower_cond.go 96.15% 3 Missing ⚠️
arm64_flags.go 93.54% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: ARM64 Go assembler instruction families

Solid, well-structured addition. The new bitfield family (BFI/BFXIL/SBFX/UBFX/SBFIZ/UBFIZ + W variants), ROR/rotate helpers, eval32/setFlagsSub32/setFlagsLogic32, validateARM64MemoryIndex, and the indexed-memory extension parsing were all checked and are correct. In particular:

  • ROR / rotateInt are safe at shift-by-0 (sub bits,0 then & (bits-1) yields 0, no shl-by-width poison).
  • Every new shift/rotate/bitfield emission is range-checked or masked, so no undefined-behavior IR is produced.
  • The parseImm reordering (trying parseImmExpr before float parsing) correctly preserves the integer meaning of expressions like $64-31, and the comment accurately explains why.

Security and performance passes found nothing blocking. CI uses pull_request (not pull_request_target) with read-only default permissions; the new shell scripts use set -euo pipefail, mktemp with cleanup traps, quoted vars, and argv-passing to Python; dependencies are version-pinned. The only performance note is the reflection-based fmt.Sprintf("%s %#v", ...) probe key in cmd/plan9asmscan/main.go (scan tooling only, not the lowering path).

A few points below. Note the build could not be run here (LLVM cgo headers are unavailable in this environment), so findings are from static analysis and the PR's own baseline JSON.

Non-blocking notes not placed inline

CMPW computes flags on 64 bits, inconsistent with the new 32-bit SUBSW (arm64_lower_arith.go:666-682, _ = op // CMPW is treated the same as CMP for now.)
This is pre-existing (outside the diff), but the PR adds a correctly 32-bit SUBSW/TSTW path, which makes the inconsistency more visible. CMPW routes both operands through eval64 and computes NZCV via setFlagsSub (64-bit). When the compared registers differ in their high 32 bits, or when the low-32 subtraction borrows/overflows differently than the 64-bit one, C/V (and N/Z) can differ from real CMPW, affecting following HS/LO/HI/LS/GE/LT/GT/LE conditions. Consider routing CMPW through eval32/setFlagsSub32 to match the new SUBSW path. Not introduced by this PR, so non-blocking.

Doc -corpus help / stale snapshot table — see inline notes.

Findings without inline locations

  • doc/plan9asm-corpus.md:212: The arm64 row of this "Go 1.27 snapshot" table is now stale relative to the go-asm-baseline.json this PR updated. The go1.27 arm64 baseline block reports unique_forms=1916, supported_forms=455, context_forms=39, unsupported_forms=1422, runtime_verified_forms=58, but this row still shows 1901 / 384 / 21 / 1496 / 0. Since the doc names testdata/coverage/go-asm-baseline.json as the authoritative source, please regenerate this table. In particular the row still claims arm64 "runtime verified: 0" while the PR adds an executable arm64 conformance case and the baseline now records 58.

Comment thread cmd/plan9asmscan/main.go Outdated
Comment thread arm64_lower_data.go
Comment thread arm64_lower_arith.go
@cpunion

cpunion commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed all review notes in 5a1cfe0: CMPW now computes all NZCV flags in i32 and the executable oracle checks Z/C/V cases; the Go 1.27 ARM64 snapshot is synchronized; the scanner help includes arm64-plan9; and negative register-to-register MOVW plus ANDSW.N are runtime-checked against both native Go and generated LLVM. I also made the complete ARM64 conformance translation run on every coverage host and added focused success/error-path tests; local root coverage is now 88.2%, and estimated patch coverage from the Go cover profile is 92.6% versus the initial Codecov 38.3%. The supplemental x/arch baseline moves one form from supported to unsupported because CMPW R24.SXTB<<5, R9 is decoder output that native go tool asm rejects with shift amount out of range 0 to 4; all 489 x/arch cases accepted by the native Go assembler remain lowerable. Full local go test, race, Go 1.20–1.27 official assembler coverage, x/arch filtering, all configured stdlib targets, all 14 klauspost/compress assembly files, and latest LLGo main go.work regressions pass.

@cpunion

cpunion commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator Author

Extended the authoritative gate in 79b80d0 to the complete LLGo Plan 9 GOARCH surface: 386, amd64, arm, arm64, and wasm. The Linux/wasm standard-library matrix now expands across every architecture setting supported by the selected Go release, while the cross-version fingerprint includes wasm via its official opcode namespace and concrete GOROOT assembly (Go has no wasm operand-class encoder table). I verified the official inventory on the latest patch releases from Go 1.20 through Go 1.27, ran representative old-version level matrices, and ran the full Go 1.27 44-target scan/translate/LLVM-object matrix locally in 352 seconds with zero unsupported forms or parse failures. The broader completion criteria are recorded in #36.

@cpunion

cpunion commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator Author

The first expanded CI run identified a host/target separation bug in the gate itself: go run rebuilt plan9asmscan under GOAMD64=v4, so GitHub runners without v4 host CPUs refused to start the scanner before any target corpus was checked. ad79ff0 builds the host scanner once before applying target settings, then passes those settings only to the scanner process and its target go list calls. This is the correct cross-compilation model and also removes 44 repeated scanner builds; the complete local 386 plus GOAMD64 v1-v4 matrix now passes in 55 seconds.

@zhouguangyuan0718 zhouguangyuan0718 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two P1 correctness issues reproduced at ad79ff0 by running the same assembly through native Go and plan9asm-generated LLVM on macOS/arm64 with Go 1.26.5. The existing go test ./... and all three ARM64 conformance tests pass, but the additional cases below produce different results. Please fix both and add executable conformance regressions before merging.

Comment thread arm64_lower_data.go Outdated
Comment thread arm64_lower_vec.go
@xushiwei
xushiwei merged commit 6fdc1d5 into xgo-dev:main Sep 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Track authoritative Plan 9 instruction and operand-form coverage for all LLGo architectures

3 participants