feat(audit): list every corpus failure per architecture
This commit is contained in:
+31
-11
@@ -38,7 +38,7 @@ import (
|
|||||||
// construction and are excluded from the diff; the other architectures list
|
// construction and are excluded from the diff; the other architectures list
|
||||||
// their conditional branches outright.
|
// their conditional branches outright.
|
||||||
func cmdAuditInstructions(args []string) error {
|
func cmdAuditInstructions(args []string) error {
|
||||||
fs := newCommand("audit-instructions", "gasm audit-instructions [--corpus [dir]] [-I dir] [amd64|arm64|riscv64|loong64]", `
|
fs := newCommand("audit-instructions", "gasm audit-instructions [--corpus [dir]] [--list] [-I dir] [amd64|arm64|riscv64|loong64]", `
|
||||||
Compare the gasm encoder for the given architecture (default amd64) against
|
Compare the gasm encoder for the given architecture (default amd64) against
|
||||||
go tool asm and print the diff: superset encodings (gasm-only, shippable via
|
go tool asm and print the diff: superset encodings (gasm-only, shippable via
|
||||||
gasm asm --format goobj) and known-but-unencodable names (the backlog). The
|
gasm asm --format goobj) and known-but-unencodable names (the backlog). The
|
||||||
@@ -55,16 +55,19 @@ toolchain probing. A file whose name carries a recognisable _arch suffix is
|
|||||||
attempted for that architecture; a file without one is attempted for all
|
attempted for that architecture; a file without one is attempted for all
|
||||||
four, exactly as a GOARCH build would compile it. The report gives the
|
four, exactly as a GOARCH build would compile it. The report gives the
|
||||||
per-architecture pass rates and the most common failure reasons, which drive
|
per-architecture pass rates and the most common failure reasons, which drive
|
||||||
the encodability backlog by frequency rather than by table order.
|
the encodability backlog by frequency rather than by table order. With
|
||||||
|
-list the report also prints every failing file with its reason, per
|
||||||
|
architecture.
|
||||||
`)
|
`)
|
||||||
corpus := fs.Bool("corpus", false, "assemble a corpus of .s files and report pass rates and failure reasons")
|
corpus := fs.Bool("corpus", false, "assemble a corpus of .s files and report pass rates and failure reasons")
|
||||||
|
list := fs.Bool("list", false, "with --corpus, list every failing file with its reason, per architecture")
|
||||||
var dirs includeDirs
|
var dirs includeDirs
|
||||||
fs.Var(&dirs, "I", "directory to search for #include files (may be repeated)")
|
fs.Var(&dirs, "I", "directory to search for #include files (may be repeated)")
|
||||||
if err := fs.Parse(args); err != nil {
|
if err := fs.Parse(args); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if *corpus {
|
if *corpus {
|
||||||
return cmdAuditCorpus(fs.Args(), dirs)
|
return cmdAuditCorpus(fs.Args(), dirs, *list)
|
||||||
}
|
}
|
||||||
archName := "amd64"
|
archName := "amd64"
|
||||||
switch n := len(fs.Args()); {
|
switch n := len(fs.Args()); {
|
||||||
@@ -403,20 +406,30 @@ type corpusTally struct {
|
|||||||
assembled int
|
assembled int
|
||||||
reasons map[string]int // failure reason → count
|
reasons map[string]int // failure reason → count
|
||||||
example map[string]string // failure reason → one representative file
|
example map[string]string // failure reason → one representative file
|
||||||
|
fails []corpusFailure // every failure, in file order, for --list
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *corpusTally) fail(path, reason string) {
|
// corpusFailure is one failed attempt, recorded for the --list report.
|
||||||
|
type corpusFailure struct {
|
||||||
|
path string
|
||||||
|
reason string
|
||||||
|
detail string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *corpusTally) fail(path string, err error) {
|
||||||
|
reason := corpusReason(err)
|
||||||
t.reasons[reason]++
|
t.reasons[reason]++
|
||||||
if t.example[reason] == "" {
|
if t.example[reason] == "" {
|
||||||
t.example[reason] = path
|
t.example[reason] = path
|
||||||
}
|
}
|
||||||
|
t.fails = append(t.fails, corpusFailure{path: path, reason: reason, detail: firstLine(err.Error())})
|
||||||
}
|
}
|
||||||
|
|
||||||
// cmdAuditCorpus implements audit-instructions --corpus. The include
|
// cmdAuditCorpus implements audit-instructions --corpus. The include
|
||||||
// directories carry #include resolution over a corpus whose files refer to
|
// directories carry #include resolution over a corpus whose files refer to
|
||||||
// headers such as GOROOT/pkg/include, the same -I a toolchain comparison
|
// headers such as GOROOT/pkg/include, the same -I a toolchain comparison
|
||||||
// needs.
|
// needs.
|
||||||
func cmdAuditCorpus(args []string, dirs includeDirs) error {
|
func cmdAuditCorpus(args []string, dirs includeDirs, list bool) error {
|
||||||
if len(args) > 1 {
|
if len(args) > 1 {
|
||||||
return &usageError{fmt.Errorf("audit-instructions --corpus takes at most one directory argument")}
|
return &usageError{fmt.Errorf("audit-instructions --corpus takes at most one directory argument")}
|
||||||
}
|
}
|
||||||
@@ -454,7 +467,7 @@ func cmdAuditCorpus(args []string, dirs includeDirs) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
printCorpusStats(stats)
|
printCorpusStats(stats, list)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -645,7 +658,7 @@ func runCorpusAudit(root string, dirs includeDirs) (*corpusStats, error) {
|
|||||||
hdrDir, err := hdr.dirFor(pkgDir, goos, goarchName(tg.a))
|
hdrDir, err := hdr.dirFor(pkgDir, goos, goarchName(tg.a))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ok = false
|
ok = false
|
||||||
t.fail(path, corpusReason(err))
|
t.fail(path, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
f, errs := parser.ParseWithOptions(path, src, parser.Options{
|
f, errs := parser.ParseWithOptions(path, src, parser.Options{
|
||||||
@@ -654,12 +667,12 @@ func runCorpusAudit(root string, dirs includeDirs) (*corpusStats, error) {
|
|||||||
})
|
})
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
ok = false
|
ok = false
|
||||||
t.fail(path, corpusReason(errs[0]))
|
t.fail(path, errs[0])
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, err := assembleFile(tg.a, f); err != nil {
|
if _, err := assembleFile(tg.a, f); err != nil {
|
||||||
ok = false
|
ok = false
|
||||||
t.fail(path, corpusReason(err))
|
t.fail(path, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
t.assembled++
|
t.assembled++
|
||||||
@@ -684,7 +697,7 @@ func runCorpusAudit(root string, dirs includeDirs) (*corpusStats, error) {
|
|||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ok = false
|
ok = false
|
||||||
t.fail(path, corpusReason(err))
|
t.fail(path, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
t.assembled++
|
t.assembled++
|
||||||
@@ -706,7 +719,7 @@ func runCorpusAudit(root string, dirs includeDirs) (*corpusStats, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// printCorpusStats renders the corpus audit report.
|
// printCorpusStats renders the corpus audit report.
|
||||||
func printCorpusStats(s *corpusStats) {
|
func printCorpusStats(s *corpusStats, list bool) {
|
||||||
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)
|
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
|
// The rate is over the files a supported build would attempt: the
|
||||||
// other ports' files sit in the count for completeness but can never
|
// other ports' files sit in the count for completeness but can never
|
||||||
@@ -721,6 +734,13 @@ func printCorpusStats(s *corpusStats) {
|
|||||||
fmt.Printf(" %4d %s\n", t.reasons[r], r)
|
fmt.Printf(" %4d %s\n", t.reasons[r], r)
|
||||||
fmt.Printf(" e.g. %s\n", t.example[r])
|
fmt.Printf(" e.g. %s\n", t.example[r])
|
||||||
}
|
}
|
||||||
|
if !list {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, f := range t.fails {
|
||||||
|
fmt.Printf(" FAIL %s\n", f.path)
|
||||||
|
fmt.Printf(" %s: %s\n", f.reason, f.detail)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -365,7 +365,7 @@ add: 16 bytes, args=24, frame=0 NOSPLIT
|
|||||||
## audit-instructions
|
## audit-instructions
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Usage: gasm audit-instructions [--corpus [dir]] [-I dir] [amd64|arm64|riscv64|loong64]
|
Usage: gasm audit-instructions [--corpus [dir]] [--list] [-I dir] [amd64|arm64|riscv64|loong64]
|
||||||
```
|
```
|
||||||
|
|
||||||
Compare the gasm encoder for the given architecture (default amd64) against the
|
Compare the gasm encoder for the given architecture (default amd64) against the
|
||||||
@@ -403,7 +403,9 @@ architecture; a file without one is attempted for all four, exactly as a
|
|||||||
The report gives the headline number (files
|
The report gives the headline number (files
|
||||||
that assemble for every target architecture), the per-architecture pass rates
|
that assemble for every target architecture), the per-architecture pass rates
|
||||||
and the most common failure reasons with one representative file each, which
|
and the most common failure reasons with one representative file each, which
|
||||||
drive the encodability backlog by frequency rather than by table order. A run
|
drive the encodability backlog by frequency rather than by table order. With
|
||||||
|
`--list` the report additionally prints every failing file with its failure
|
||||||
|
reason, per architecture. A run
|
||||||
over GOROOT takes under a second.
|
over GOROOT takes under a second.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
.TH GASM-AUDIT-INSTRUCTIONS 1 "2026-09-19" "gasm" "User Commands"
|
.TH GASM-AUDIT-INSTRUCTIONS 1 "2026-09-21" "gasm" "User Commands"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
gasm-audit-instructions \- diff the encoder against the Go toolchain, or measure a corpus
|
gasm-audit-instructions \- diff the encoder against the Go toolchain, or measure a corpus
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B gasm audit\-instructions [\-\-corpus [\fIdir\fR]] [\-I dir] [amd64|arm64|riscv64|loong64]
|
.B gasm audit\-instructions [\-\-corpus [\fIdir\fR]] [\-\-list] [\-I dir] [amd64|arm64|riscv64|loong64]
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
Compare the gasm encoder for the given architecture (default amd64)
|
Compare the gasm encoder for the given architecture (default amd64)
|
||||||
against
|
against
|
||||||
@@ -39,6 +39,12 @@ second.
|
|||||||
Assemble a corpus of .s files and report pass rates and failure
|
Assemble a corpus of .s files and report pass rates and failure
|
||||||
reasons.
|
reasons.
|
||||||
.TP
|
.TP
|
||||||
|
.B \-\-list
|
||||||
|
With
|
||||||
|
.BR \-\-corpus ,
|
||||||
|
print every failing file with its failure reason, per architecture,
|
||||||
|
instead of one representative file per reason.
|
||||||
|
.TP
|
||||||
.B \-I \fIdir\fR
|
.B \-I \fIdir\fR
|
||||||
Directory to search for #include files; may be repeated, searched in
|
Directory to search for #include files; may be repeated, searched in
|
||||||
order after the source directory. A corpus run whose files include
|
order after the source directory. A corpus run whose files include
|
||||||
|
|||||||
Reference in New Issue
Block a user