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