feat(audit): probe the new operand shapes and measure attemptable files

Assisted-by: GLM 5.3 Flash
This commit is contained in:
2026-09-20 06:45:03 +02:00
parent de5d9f358e
commit 5399a8a724
5 changed files with 119 additions and 15 deletions
+97 -15
View File
@@ -266,18 +266,62 @@ func probeShapes(a arch.Arch) []string {
// and takes R register spellings.
"EQ, R0, R1, R2", "EQ, R0, R1", "EQ, R0",
"GE, F0, F1, F2", "NE, F0, F1, $0",
// Pairs, acquire/release and exclusive atomics, LSE-AL forms.
"(R0), R1", "R0, (R1)", "R1, (R2), R3", "(R2, R3), 8(R1)",
"8(R1), (R2, R3)", "R1, R2, (R3)", "(R0)",
// System operations and their register/operand names.
"$4, R1, p2", "$35943", "$1", "$1, SPSel", "SPSel, R0",
"IVAC, R0", "(R0), PLDL1KEEP", "R1, R2, R3, R4",
// SIMD element, structure and literal-pool forms.
"(R0), [V1.B16]", "[V1.B16], (R0)", "V13.S[0], R1",
"R1, V2.B[3]", "$4, V1.B16, V2.B16", "V1.B16, (R0)",
"(R0), V1.B16", "",
// The spellings GOROOT's own kernels use, from the
// differential kernels this table was proven against.
"R0, p2", "R0, R1", "F0, F1, F2, F3", "$4, V1.B16, V2.B16, V3.B16, V4.B16",
"(R0), [V0.B8, V1.B8, V2.B8, V3.B8]", "$1, $2, V1",
"R0, R1, p2", "p2, R1", "$1234, R1", "DCZID_EL0, R1",
"$0", "R1, $4, EQ", "$33, R1, $25, R2", "$4, R1, p2",
"$4, V1.B8, V2.B8, V3.B8", "$63, V1.D2, V2.D2, V3.D2",
"V1.B16, [V2.B16], V3.B16", "V1.B8, [V2.B16, V3.B16], V4.B8",
"$4, V1.B16, V2.B16, V3.B16", "$15, V1", "V1, V2, p2",
"R0, R1, $1, $4, p2",
}
case arch.RISCV:
return []string{
"X5, X6, X7", "X5, X6", "X5", "$1, X5", "X5, (X6)", "$1, X5, X6",
"(X5), X6", "F0, F1, F2", "F0, F1", "p2", "X1, p2", "X0, p2",
"X5, X6, p2", "p2(SB)",
// AMO atomics: destination, base, source.
"R5, (R4), R6", "X5, (X4), X6",
// Segment stores take the first vector register aligned
// to the segment count, as the toolchain requires.
"(X5), X6, V0, V8", "(X5), X6, V0", "(X5), X0, V4",
// The FP multiply-add family takes four registers.
"F0, F1, F2, F3",
// The RVV slice: register, vector-register and vtype forms.
"V1, V2, V3", "V1, X5, V2", "V1", "V1, (X5)", "(X5), V1",
"$15, V1", "$15", "V1, V2", "V1, X5",
"X5, X6, p2", "R5, R6, p2",
"X5, E8, M8, TA, MA, X6", "$4, E32, M1, TA, MA, X1",
"(X5), X6, V1, V2",
"",
}
case arch.LOONG64:
return []string{
"R4, R5, R6", "R4, R5", "R4", "$1, R4", "R4, (R5)", "(R4), R5",
"F0, F1, F2", "F0, F1", "p2", "R1, p2", "R4, p2",
"$1, R4, R5, R6", "$65536, R4", "R4, R5, p2", "p2(SB)",
// AMO atomics: destination, base, source.
"R5, (R4), R6", "X5, (X4), X6",
// Segment stores take the first vector register aligned
// to the segment count, as the toolchain requires.
"(X5), X6, V0, V8", "(X5), X6, V0", "(X5), X0, V4",
// The LSX and LASX banks share the 5-bit numbering with F.
"V1, V2, V3", "X1, X2, X3", "V1, V2", "X1, X2", "V1", "X1",
// The vector compare-to-flag forms land in an FCC register.
"V1, FCC0", "X1, FCC0",
"",
}
}
return nil
@@ -376,15 +420,40 @@ func cmdAuditCorpus(args []string) error {
// corpusStats is the outcome of one corpus audit run.
type corpusStats struct {
root string
files int
generic int // files attempted for all four architectures
full int // files that assembled for every target architecture
targets []corpusTarget
tallies []*corpusTally
root string
files int
generic int // files attempted for all four architectures
otherPort int // files named for another Go port: never attempted
full int // files that assembled for every target architecture
targets []corpusTarget
tallies []*corpusTally
}
// runCorpusAudit assembles every .s file under root and returns the stats.
// goPortSuffixes lists every architecture the Go project ports to. A file
// named for one of them belongs to that port's build, not to the generic
// set, even when gasm does not support the architecture.
var goPortSuffixes = []string{
"386", "amd64", "arm", "arm64", "loong64", "mips", "mips64",
"mips64le", "mipsle", "ppc64", "ppc64le", "riscv", "riscv64",
"s390x", "wasm",
}
// otherPortFile reports whether the file's name carries a Go-architecture
// suffix gasm does not support.
func otherPortFile(path string) bool {
base := path
if i := strings.LastIndexByte(base, '/'); i >= 0 {
base = base[i+1:]
}
for _, sfx := range goPortSuffixes {
if strings.HasSuffix(base, "_"+sfx+".s") {
return true
}
}
return false
}
func runCorpusAudit(root string) (*corpusStats, error) {
files, err := asmFiles(root)
if err != nil {
@@ -403,7 +472,7 @@ func runCorpusAudit(root string) (*corpusStats, error) {
}
// full is the north-star number: a file counts when every architecture
// its name allows assembles it.
full, generic := 0, 0
full, generic, otherPort := 0, 0, 0
for _, path := range files {
src, err := readSource(path)
@@ -419,6 +488,13 @@ func runCorpusAudit(root string) (*corpusStats, error) {
wanted = append(wanted, i)
}
}
} else if otherPortFile(path) {
// A file named for a Go port gasm does not support (arm,
// 386, s390x, ...) is compiled by no supported-arch build,
// so it is neither generic nor a per-arch attempt: counting
// it as generic would make the headline unreachably low
// for reasons no supported target can fix.
otherPort++
} else {
generic++
for i := range targets {
@@ -449,19 +525,25 @@ func runCorpusAudit(root string) (*corpusStats, error) {
}
return &corpusStats{
root: root,
files: len(files),
generic: generic,
full: full,
targets: targets,
tallies: tallies,
root: root,
files: len(files),
generic: generic,
otherPort: otherPort,
full: full,
targets: targets,
tallies: tallies,
}, nil
}
// printCorpusStats renders the corpus audit report.
func printCorpusStats(s *corpusStats) {
fmt.Printf("corpus %s: %d files (%d generic, attempted for all architectures)\n", s.root, s.files, s.generic)
fmt.Printf(" assemble for every target architecture: %d (%.1f%%)\n", s.full, 100*float64(s.full)/float64(max(s.files, 1)))
fmt.Printf("corpus %s: %d files (%d generic, attempted for all architectures; %d named for other Go ports, never attempted)\n", s.root, s.files, s.generic, s.otherPort)
// The rate is over the files a supported build would attempt: the
// other ports' files sit in the count for completeness but can never
// assemble, so counting them in the denominator would report the gap
// of architectures gasm deliberately does not target.
attemptable := max(s.files-s.otherPort, 1)
fmt.Printf(" assemble for every target architecture: %d of %d attemptable (%.1f%%)\n", s.full, attemptable, 100*float64(s.full)/float64(attemptable))
for i, tg := range s.targets {
t := s.tallies[i]
fmt.Printf(" %s: %d/%d attempted\n", tg.name, t.assembled, t.attempted)
+7
View File
@@ -26,6 +26,13 @@ func TestGroundTruthARM64(t *testing.T) {
"../testdata/verify/bigframe_arm64.s",
"../testdata/verify/guard_arm64.s",
"../testdata/verify/indirect_arm64.s",
"../testdata/verify/exclusive_arm64.s",
"../testdata/verify/shifts_arm64.s",
"../testdata/verify/atomics_arm64.s",
"../testdata/verify/crypto_arm64.s",
"../testdata/verify/integer_arm64.s",
"../testdata/verify/simd_arm64.s",
"../testdata/verify/system_arm64.s",
} {
t.Run(path, func(t *testing.T) {
src, err := os.ReadFile(path)
+7
View File
@@ -115,6 +115,13 @@ func TestGroundTruthAMD64(t *testing.T) {
"../testdata/verify/bigframe_amd64.s",
"../testdata/verify/guard_amd64.s",
"../testdata/verify/indirect_amd64.s",
"../testdata/verify/widen_amd64.s",
"../testdata/verify/scalar_amd64.s",
"../testdata/verify/atomics_amd64.s",
"../testdata/verify/system_amd64.s",
"../testdata/verify/crypto_amd64.s",
"../testdata/verify/sse_amd64.s",
"../testdata/verify/avx_amd64.s",
} {
t.Run(path, func(t *testing.T) {
f, errs := parser.Parse(path, mustRead(t, path))
+4
View File
@@ -25,6 +25,10 @@ func TestGroundTruthLOONG64(t *testing.T) {
"../testdata/verify/bigframe_loong64.s",
"../testdata/verify/guard_loong64.s",
"../testdata/verify/indirect_loong64.s",
"../testdata/verify/movwfp_loong64.s",
"../testdata/verify/branchu_loong64.s",
"../testdata/verify/atomics_loong64.s",
"../testdata/verify/vector_loong64.s",
"trampoline_loong64.s",
} {
t.Run(path, func(t *testing.T) {
+4
View File
@@ -29,6 +29,10 @@ func TestGroundTruthRISCV(t *testing.T) {
"../testdata/verify/guard_riscv64.s",
"../testdata/verify/indirect_riscv64.s",
"../testdata/verify/misc_riscv64.s",
"../testdata/verify/rvcstore_riscv64.s",
"../testdata/verify/atomics_riscv64.s",
"../testdata/verify/vector_riscv64.s",
"../testdata/verify/bitmanip_riscv64.s",
"trampoline_riscv64.s",
} {
t.Run(path, func(t *testing.T) {