diff --git a/cmd/gasm/audit.go b/cmd/gasm/audit.go index 3b8ca6b..50110b0 100644 --- a/cmd/gasm/audit.go +++ b/cmd/gasm/audit.go @@ -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) diff --git a/verify/arm64_groundtruth_test.go b/verify/arm64_groundtruth_test.go index 7db2f92..5972782 100644 --- a/verify/arm64_groundtruth_test.go +++ b/verify/arm64_groundtruth_test.go @@ -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) diff --git a/verify/groundtruth_test.go b/verify/groundtruth_test.go index d411356..427c225 100644 --- a/verify/groundtruth_test.go +++ b/verify/groundtruth_test.go @@ -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)) diff --git a/verify/l64_groundtruth_test.go b/verify/l64_groundtruth_test.go index 548bc3e..326d8a9 100644 --- a/verify/l64_groundtruth_test.go +++ b/verify/l64_groundtruth_test.go @@ -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) { diff --git a/verify/riscv_groundtruth_test.go b/verify/riscv_groundtruth_test.go index 6c6d85b..0b8a981 100644 --- a/verify/riscv_groundtruth_test.go +++ b/verify/riscv_groundtruth_test.go @@ -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) {