Source file src/simd/archsimd/_gen/cmd/specstats/report.go

     1  // Copyright 2026 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     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