internal/simdgen: remove map-iteration dependence from output

This makes checking for (lack of) effects from changes
much easier.

Change-Id: I0b8c49381798d924541abb95bbfcbe8281d37950
Reviewed-on: https://go-review.googlesource.com/c/arch/+/680178
Reviewed-by: Cherry Mui <cherryyz@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/internal/simdgen/gen_simdTypes.go b/internal/simdgen/gen_simdTypes.go
index d19c8d2..0405c58 100644
--- a/internal/simdgen/gen_simdTypes.go
+++ b/internal/simdgen/gen_simdTypes.go
@@ -6,6 +6,8 @@
 
 import (
 	"fmt"
+	"slices"
+	"sort"
 	"strings"
 )
 
@@ -20,6 +22,14 @@
 	Size                    int    // The size of the type
 }
 
+func compareSimdTypes(x, y simdType) int {
+	c := strings.Compare(x.Name, y.Name)
+	if c != 0 {
+		return c
+	}
+	return strings.Compare(x.Type, y.Type)
+}
+
 type simdTypeMap map[int][]simdType
 
 type simdTypePair struct {
@@ -27,44 +37,51 @@
 	Tdst simdType
 }
 
-const simdTypesTmpl = `// Code generated by x/arch/internal/simdgen using 'go run . -xedPath $XED_PATH -o godefs -goroot $GOROOT go.yaml types.yaml categories.yaml'; DO NOT EDIT.
+func compareSimdTypePairs(x, y simdTypePair) int {
+	c := compareSimdTypes(x.Tsrc, y.Tsrc)
+	if c != 0 {
+		return c
+	}
+	return compareSimdTypes(x.Tdst, y.Tdst)
+}
+
+const simdTypesTemplates = `{{define "fileHeader"}}// Code generated by x/arch/internal/simdgen using 'go run . -xedPath $XED_PATH -o godefs -goroot $GOROOT go.yaml types.yaml categories.yaml'; DO NOT EDIT.
 
 //go:build goexperiment.simd
 
 package simd
+{{end}}
 
-{{- range $size, $ts := .TypeMap }}
+{{define "sizeTmpl"}}
+// v{{.}} is a tag type that tells the compiler that this is really {{.}}-bit SIMD
+type v{{.}} struct {
+	_{{.}} struct{}
+}
+{{end}}
 
-// v{{$size}} is a tag type that tells the compiler that this is really {{$size}}-bit SIMD
-type v{{$size}} struct {
-	_{{$size}} struct{}
+{{define "typeTmpl"}}
+// {{.Name}} is a {{.Size}}-bit SIMD vector of {{.Lanes}} {{.Base}}
+type {{.Name}} struct {
+{{.Fields}}
 }
 
-{{- range $i, $tsrc := $ts }}
+{{- if ne .Type "mask"}}
 
-// {{$tsrc.Name}} is a {{$size}}-bit SIMD vector of {{$tsrc.Lanes}} {{$tsrc.Base}}
-type {{$tsrc.Name}} struct {
-{{$tsrc.Fields}}
-}
+// Len returns the number of elements in a {{.Name}}
+func (x {{.Name}}) Len() int { return {{.Lanes}} }
 
-{{- if ne $tsrc.Type "mask"}}
-
-// Len returns the number of elements in a {{$tsrc.Name}}
-func (x {{$tsrc.Name}}) Len() int { return {{$tsrc.Lanes}} }
-
-// Load{{$tsrc.Name}} loads a {{$tsrc.Name}} from an array
+// Load{{.Name}} loads a {{.Name}} from an array
 //
 //go:noescape
-func Load{{$tsrc.Name}}(y *[{{$tsrc.Lanes}}]{{$tsrc.Base}}) {{$tsrc.Name}}
+func Load{{.Name}}(y *[{{.Lanes}}]{{.Base}}) {{.Name}}
 
-// Store stores a {{$tsrc.Name}} to an array
+// Store stores a {{.Name}} to an array
 //
 //go:noescape
-func (x {{$tsrc.Name}}) Store(y *[{{$tsrc.Lanes}}]{{$tsrc.Base}})
+func (x {{.Name}}) Store(y *[{{.Lanes}}]{{.Base}})
 
 {{- end}}
-{{- end}}
-{{- end}}
+{{end}}
 `
 
 const simdStubsTmpl = `// Code generated by x/arch/internal/simdgen using 'go run . -xedPath $XED_PATH -o godefs -goroot $GOROOT go.yaml types.yaml categories.yaml'; DO NOT EDIT.
@@ -168,6 +185,7 @@
 			}
 		}
 	}
+	slices.SortFunc(v, compareSimdTypePairs)
 	return v
 }
 
@@ -180,25 +198,38 @@
 			}
 		}
 	}
+	slices.SortFunc(m, compareSimdTypes)
 	return m
 }
 
 // writeSIMDTypes generates the simd vector type and writes it to types_amd64.go
 // within the specified directory.
 func writeSIMDTypes(directory string, typeMap simdTypeMap) error {
-	file, t, err := openFileAndPrepareTemplate(directory, "src/"+simdPackage+"/types_amd64.go", simdTypesTmpl)
+	file, t, err := openFileAndPrepareTemplate(directory, "src/"+simdPackage+"/types_amd64.go", simdTypesTemplates)
 	if err != nil {
 		return err
 	}
 	defer file.Close()
 
-	type templateData struct {
-		TypeMap simdTypeMap
+	if err := t.ExecuteTemplate(file, "fileHeader", nil); err != nil {
+		return fmt.Errorf("failed to execute fileHeader template: %w", err)
 	}
 
-	err = t.Execute(file, templateData{typeMap})
-	if err != nil {
-		return fmt.Errorf("failed to execute template: %w", err)
+	sizes := make([]int, 0, len(typeMap))
+	for size := range typeMap {
+		sizes = append(sizes, size)
+	}
+	sort.Ints(sizes)
+
+	for _, size := range sizes {
+		if err := t.ExecuteTemplate(file, "sizeTmpl", size); err != nil {
+			return fmt.Errorf("failed to execute size template for size %d: %w", size, err)
+		}
+		for _, typeDef := range typeMap[size] {
+			if err := t.ExecuteTemplate(file, "typeTmpl", typeDef); err != nil {
+				return fmt.Errorf("failed to execute type template for type %s: %w", typeDef.Name, err)
+			}
+		}
 	}
 
 	return nil
diff --git a/internal/simdgen/gen_utility.go b/internal/simdgen/gen_utility.go
index 28a451d..7fcade6 100644
--- a/internal/simdgen/gen_utility.go
+++ b/internal/simdgen/gen_utility.go
@@ -355,6 +355,7 @@
 		}
 		deduped = append(deduped, dup[0])
 	}
+	slices.SortFunc(deduped, compareOperations)
 	return deduped, nil
 }
 
diff --git a/internal/simdgen/godefs.go b/internal/simdgen/godefs.go
index 2a611c9..9309e0c 100644
--- a/internal/simdgen/godefs.go
+++ b/internal/simdgen/godefs.go
@@ -6,6 +6,8 @@
 
 import (
 	"log"
+	"slices"
+	"strings"
 
 	"golang.org/x/arch/internal/unify"
 )
@@ -30,6 +32,47 @@
 	Masked *string
 }
 
+func compareStringPointers(x, y *string) int {
+	if x != nil && y != nil {
+		return strings.Compare(*x, *y)
+	}
+	if x == nil && y == nil {
+		return 0
+	}
+	if x == nil {
+		return -1
+	}
+	return 1
+}
+
+func compareOperations(x, y Operation) int {
+	if c := strings.Compare(x.Go, y.Go); c != 0 {
+		return c
+	}
+	if c := strings.Compare(x.GoArch, y.GoArch); c != 0 {
+		return c
+	}
+	if len(x.In) < len(y.In) {
+		return -1
+	}
+	if len(x.In) > len(y.In) {
+		return 1
+	}
+	if len(x.Out) < len(y.Out) {
+		return -1
+	}
+	if len(x.Out) > len(y.Out) {
+		return 1
+	}
+	for i := range x.In {
+		ox, oy := &x.In[i], y.In[i]
+		if c := compareStringPointers(ox.Go, oy.Go); c != 0 {
+			return c
+		}
+	}
+	return 0
+}
+
 type Operand struct {
 	Class string // One of "mask", "immediate", "vreg" and "mem"
 
@@ -68,6 +111,7 @@
 		op.sortOperand()
 		ops = append(ops, op)
 	}
+	slices.SortFunc(ops, compareOperations)
 	// The parsed XED data might contain duplicates, like
 	// 512 bits VPADDP.
 	deduped := dedup(ops)