1
2
3
4
5 package main
6
7 import (
8 "bytes"
9 "fmt"
10 "strings"
11 )
12
13 func (m *metrics) report(w *bytes.Buffer) {
14 fmt.Fprintf(w, "SIMD spec transition progress\n\n")
15 fmt.Fprintf(w, "Produced by go run ./cmd/specstats in archsimd/_gen.\n\n")
16 fmt.Fprintf(w, "Every fraction heads for 100%%; every count heads for zero. Bracketed\n")
17 fmt.Fprintf(w, "names are SPEC-TRANSITION.md tasks that move the figure. Indented,\n")
18 fmt.Fprintf(w, "untagged numbers are context breaking down the figure above them.\n")
19 fmt.Fprintf(w, "String, Len, ToArch, and FromArch are excluded throughout (§2.7), as are\n")
20 fmt.Fprintf(w, "declarations that are not vector operations, such as CPU feature checks,\n")
21 fmt.Fprintf(w, "and the deprecated amd64 Merge (see nonSpecDecl).\n")
22
23 m.summary(w)
24 m.coverage(w)
25 m.signatures(w)
26 m.documentation(w)
27 m.retirement(w)
28 m.disagreement(w)
29 }
30
31 func (m *metrics) summary(w *bytes.Buffer) {
32 section(w, "Summary", "")
33
34 note(w, "Heading for 100%:")
35 frac(w, "spec coverage, by (receiver, name) (§1)", m.covPair.result(), "spec-common, spec-all")
36 frac(w, "spec-covered docs taken from spec (§3)", m.fromSpec.result(), "gen-docs, comments-yaml, handwritten-fill, template-docs")
37
38 note(w, "\nHeading for zero:")
39 count(w, "type mismatches with spec (§2)", m.typeOK.result().miss(), "fill-enforce")
40 count(w, "type divergence across targets (§2)", m.typeSame.result().miss(), "fill-enforce")
41 count(w, "spec coverage gaps in defined names (§5)", m.apiOnly, "spec-all, convert-lo, mask-bits")
42 count(w, "hand-written doc conflicts with spec (§3)", m.handConflict, "doc-triage")
43 count(w, "simd mirror drift (§3)", m.mirror.result().miss(), "comments-yaml")
44 count(w, "categories.yaml entries (§4)", m.catEntries, "cat-rehome, compiler-ops-impl, cat-delete")
45 count(w, "midway/comments.yaml entries spec covers (§4)", m.commentsSpecCovered, "comments-yaml, spec-all")
46 }
47
48 func (m *metrics) coverage(w *bytes.Buffer) {
49 section(w, "1. Spec coverage of API", "spec-common, spec-all")
50
51 frac(w, "by (target, receiver, name)", m.covTriple.result(), "")
52 frac(w, "by (receiver, name)", m.covPair.result(), "")
53 frac(w, "by name", m.covName.result(), "")
54
55 note(w, "\nBy build configuration:")
56 for _, bc := range buildConfigs {
57 frac(w, " "+bc.name, m.covTarget.result(bc.name), "")
58 }
59
60 note(w, "\nBy generator (declarations per tool, counted once per tool per pair):")
61 for _, g := range m.covGen.dims() {
62 if strings.Contains(g, "/") {
63 continue
64 }
65 frac(w, " "+g, m.covGen.result(g), "")
66 }
67
68 note(w, "\nHigh-drift-risk names (emitted by >1 generator):")
69 frac(w, " covered", m.covMulti.result(), "spec-all")
70 frac(w, " portable core (simd_emulated.go methods)", m.covCommon.result(), "spec-common")
71
72 fmt.Fprintf(w, "\n")
73 kv(w, "spec operations written so far", len(m.ops))
74 kv(w, " expanding to distinct names", m.specNames)
75 kv(w, " expanding to (receiver, name) pairs", m.specPairs)
76 }
77
78 func (m *metrics) signatures(w *bytes.Buffer) {
79 section(w, "2. Signatures and names", "named-results, fill-enforce")
80
81 note(w, `Spec/API disagreement, for declarations spec covers. As in specdoc.Fill,
82 a named argument whose name differs from spec's is a mismatch; an unnamed
83 one (or _) is not, and is counted below as a name Fill fills in instead:`)
84 count(w, " type mismatches", m.typeOK.result().miss(), "fill-enforce")
85 count(w, " parameter-name mismatches", m.paramNameOK.result().miss(), "named-results")
86 count(w, " result-name mismatches", m.resultNameOK.result().miss(), "named-results")
87
88 note(w, "\nNames spec gives that the API leaves unnamed, for declarations spec covers:")
89 count(w, " declarations with unnamed parameters", m.paramsFilled.result().miss(), "named-results")
90 count(w, " declarations with unnamed results", m.resultsFilled.result().miss(), "named-results")
91
92 note(w, `
93 API consistency across targets (the "one meaning" invariant), for
94 (receiver, name) pairs declared for more than one target. An unnamed
95 argument agrees with any name:`)
96 count(w, " divergent parameter or result types", m.typeSame.result().miss(), "fill-enforce")
97 count(w, " divergent parameter names", m.paramNameSame.result().miss(), "fill-enforce, spec-all")
98 count(w, " divergent result names", m.resultNameSame.result().miss(), "named-results")
99 }
100
101 func (m *metrics) documentation(w *bytes.Buffer) {
102 section(w, "3. Documentation", "gen-docs, comments-yaml, handwritten-fill, template-docs")
103 note(w, `Of the declarations of (receiver, name) pairs spec covers, counting each
104 file a pair is declared in, how many take their doc from spec: their doc
105 begins with spec's text (a prefix test, because Fill leaves generator notes
106 below). Pairs spec does not cover yet are not counted, and their docs still
107 come from categories.yaml, comments.yaml or hand-written text, so this can
108 read 100% while spec coverage (§1) does not.`)
109
110 frac(w, "covered declarations whose doc comes from spec", m.fromSpec.result(), "gen-docs, comments-yaml, handwritten-fill, template-docs")
111 fmt.Fprintf(w, "\n")
112 frac(w, "simd ∩ archsimd methods with matching docs", m.crossPkg.result(), "gen-docs, comments-yaml")
113 fmt.Fprintf(w, "\n")
114 count(w, "hand-written doc conflicts with spec", m.handConflict, "doc-triage")
115 for _, fc := range m.handConflictByFile {
116 count(w, " "+fc.file, fc.n, "doc-triage")
117 }
118 count(w, "drifted docs in simd mirror (stubs vs emulated)", m.mirror.result().miss(), "comments-yaml")
119 kv(w, " mirrored declarations", m.mirror.result().d)
120 }
121
122 func (m *metrics) retirement(w *bytes.Buffer) {
123 section(w, "4. Retiring the old inputs", "comments-yaml, cat-rehome, compiler-ops-impl, cat-delete")
124 note(w, `categories.yaml's exported entries are emptied field by field: template-docs
125 removes documentation, cat-rehome removes the rest, sig-from-spec removes
126 commutative, and cat-delete removes the file. Compiler-only entries are
127 handled wholesale by compiler-ops-impl.`)
128
129 count(w, "categories.yaml entries", m.catEntries, "cat-rehome, compiler-ops-impl, cat-delete")
130 sub(w, "unique operation names", m.catUniq)
131 sub(w, " exported", m.catExported)
132 count(w, "compiler-only categories.yaml entries", m.catCompilerOnly, "compiler-ops-impl")
133 fmt.Fprintf(w, "\n exported entries, by field:\n")
134 for _, k := range sortedKeys(m.catExportedFields) {
135 if k != "go" {
136 tag := "cat-rehome"
137 if k == "commutative" {
138 tag = "sig-from-spec"
139 }
140 countIndented(w, k, m.catExportedFields[k], tag)
141 }
142 }
143 fmt.Fprintf(w, "\n")
144 count(w, "midway/comments.yaml entries spec covers", m.commentsSpecCovered, "comments-yaml, spec-all")
145 fmt.Fprintf(w, "\n")
146 count(w, "commutativity disagreements, spec vs categories.yaml", m.commutDrift, "spec-all")
147 }
148
149 func (m *metrics) disagreement(w *bytes.Buffer) {
150 section(w, "5. Spec/API coverage disagreement detail", "spec-all, convert-lo, mask-bits")
151 note(w, `Where both sides define a name, receivers only the API declares are spec
152 bugs (a constraint is wrong or a sibling function is missing). Receivers
153 only spec generates are a review queue -- spec is deliberately maximal.`)
154
155 count(w, "spec misses receivers of a name it defines", m.apiOnly, "spec-all, convert-lo, mask-bits")
156 count(w, " declarations affected, counting each target", m.apiOnlyDecls, "spec-all, convert-lo, mask-bits")
157 count(w, "spec operations implemented nowhere", len(m.specOnly), "spec-all")
158 emptyList(w, len(m.specOnly))
159 for _, n := range m.specOnly {
160 fmt.Fprintf(w, " %s\n", n)
161 }
162
163 fmt.Fprintf(w, "\nEvery name either side defines, less the %d spec has never defined at all\n",
164 m.rel[relAPIOnly].names)
165 fmt.Fprintf(w, "(%d receivers, counted as coverage in §1 rather than disagreement):\n\n",
166 m.rel[relAPIOnly].apiOnly)
167 fmt.Fprintf(w, " %-14s %7s %14s %14s\n", "relation", "names", "Σ|api - spec|", "Σ|spec - api|")
168 fmt.Fprintf(w, " %-14s %7s %14s %14s\n", "", "", "(-> 0)", "(review)")
169 var total relTotal
170 for r := relEqual; r <= relSpecOnly; r++ {
171 t := m.rel[r]
172 fmt.Fprintf(w, " %-14s %7d %14d %14d\n", relationName[r], t.names, t.apiOnly, t.specOnly)
173 total.names, total.apiOnly, total.specOnly = total.names+t.names, total.apiOnly+t.apiOnly, total.specOnly+t.specOnly
174 }
175 fmt.Fprintf(w, " %-14s %7d %14d %14d\n", "total", total.names, total.apiOnly, total.specOnly)
176
177 note(w, "\nNames that disagree, with api - spec receivers (the defects):")
178 fmt.Fprintf(w, " %-26s %-11s %5s %6s %12s %12s\n",
179 "name", "relation", "|api|", "|spec|", "|api - spec|", "|spec - api|")
180 n := 0
181 for _, r := range m.names {
182 if r.rel == relEqual || r.rel == relAPIOnly {
183 continue
184 }
185 n++
186 fmt.Fprintf(w, " %-26s %-11s %5d %6d %12d %12d\n",
187 r.name, relationName[r.rel], r.api, r.spec, len(r.apiOnly), r.specOnly)
188 wrap(w, " ", "api - spec = ", r.apiOnly)
189 }
190 emptyList(w, n)
191 }
192
View as plain text