|
|
|
@@ -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)
|
|
|
|
|