| // Copyright 2025 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package main |
| |
| import ( |
| "bytes" |
| "fmt" |
| "slices" |
| "sort" |
| "strings" |
| ) |
| |
| type simdType struct { |
| Name string // The go type name of this simd type, for example Int32x4. |
| Lanes int // The number of elements in this vector/mask. |
| Base string // The element's type, like for Int32x4 it will be int32. |
| Fields string // The struct fields, it should be right formatted. |
| Type string // Either "mask" or "vreg" |
| VectorCounterpart string // For mask use only: just replacing the "Mask" in [simdType.Name] with "Int" |
| ReshapedVectorWithAndOr string // For mask use only: vector AND and OR are only available in some shape with element width 32. |
| Size int // The size of the type |
| } |
| |
| func compareSimdTypes(x, y simdType) int { |
| // "vreg" then "mask" |
| if c := -compareNatural(x.Type, y.Type); c != 0 { |
| return c |
| } |
| // want "flo" < "int" < "uin" (and then 8 < 16 < 32 < 64), |
| // not "int16" < "int32" < "int64" < "int8") |
| // so limit comparison to first 3 bytes in string. |
| if c := compareNatural(x.Base[:3], y.Base[:3]); c != 0 { |
| return c |
| } |
| // base type size, 8 < 16 < 32 < 64 |
| if c := x.Size/x.Lanes - y.Size/y.Lanes; c != 0 { |
| return c |
| } |
| // vector size last |
| return x.Size - y.Size |
| } |
| |
| type simdTypeMap map[int][]simdType |
| |
| type simdTypePair struct { |
| Tsrc simdType |
| Tdst simdType |
| } |
| |
| 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}} |
| |
| {{define "sizeTmpl"}} |
| // v{{.}} is a tag type that tells the compiler that this is really {{.}}-bit SIMD |
| type v{{.}} struct { |
| _{{.}} struct{} |
| } |
| {{end}} |
| |
| {{define "typeTmpl"}} |
| // {{.Name}} is a {{.Size}}-bit SIMD vector of {{.Lanes}} {{.Base}} |
| type {{.Name}} struct { |
| {{.Fields}} |
| } |
| |
| {{- if ne .Type "mask"}} |
| |
| // Len returns the number of elements in a {{.Name}} |
| func (x {{.Name}}) Len() int { return {{.Lanes}} } |
| |
| // Load{{.Name}} loads a {{.Name}} from an array |
| // |
| //go:noescape |
| func Load{{.Name}}(y *[{{.Lanes}}]{{.Base}}) {{.Name}} |
| |
| // Store stores a {{.Name}} to an array |
| // |
| //go:noescape |
| func (x {{.Name}}) Store(y *[{{.Lanes}}]{{.Base}}) |
| |
| {{- end}} |
| {{end}} |
| ` |
| |
| const simdStubsTmpl = `{{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}} |
| |
| {{define "op1"}} |
| {{if .Documentation}}{{.Documentation}} |
| //{{end}} |
| // Asm: {{.Asm}}, CPU Feature: {{.CPUFeature}} |
| func ({{.Op0NameAndType "x"}}) {{.Go}}() {{.GoType}} |
| {{end}} |
| |
| {{define "op2"}} |
| {{if .Documentation}}{{.Documentation}} |
| //{{end}} |
| // Asm: {{.Asm}}, CPU Feature: {{.CPUFeature}} |
| func ({{.Op0NameAndType "x"}}) {{.Go}}({{.Op1NameAndType "y"}}) {{.GoType}} |
| {{end}} |
| |
| {{define "op2_21"}} |
| {{if .Documentation}}{{.Documentation}} |
| //{{end}} |
| // Asm: {{.Asm}}, CPU Feature: {{.CPUFeature}} |
| func ({{.Op1NameAndType "x"}}) {{.Go}}({{.Op0NameAndType "y"}}) {{.GoType}} |
| {{end}} |
| |
| {{define "op2_21Type1"}} |
| {{if .Documentation}}{{.Documentation}} |
| //{{end}} |
| // Asm: {{.Asm}}, CPU Feature: {{.CPUFeature}} |
| func ({{.Op1NameAndType "x"}}) {{.Go}}({{.Op0NameAndType "y"}}) {{.GoType}} |
| {{end}} |
| |
| {{define "op3"}} |
| {{if .Documentation}}{{.Documentation}} |
| //{{end}} |
| // Asm: {{.Asm}}, CPU Feature: {{.CPUFeature}} |
| func ({{.Op0NameAndType "x"}}) {{.Go}}({{.Op1NameAndType "y"}}, {{.Op2NameAndType "z"}}) {{.GoType}} |
| {{end}} |
| |
| {{define "op3_31"}} |
| {{if .Documentation}}{{.Documentation}} |
| //{{end}} |
| // Asm: {{.Asm}}, CPU Feature: {{.CPUFeature}} |
| func ({{.Op2NameAndType "x"}}) {{.Go}}({{.Op1NameAndType "y"}}, {{.Op0NameAndType "z"}}) {{.GoType}} |
| {{end}} |
| |
| {{define "op3_21"}} |
| {{if .Documentation}}{{.Documentation}} |
| //{{end}} |
| // Asm: {{.Asm}}, CPU Feature: {{.CPUFeature}} |
| func ({{.Op1NameAndType "x"}}) {{.Go}}({{.Op0NameAndType "y"}}, {{.Op2NameAndType "z"}}) {{.GoType}} |
| {{end}} |
| |
| {{define "op3_21Type1"}} |
| {{if .Documentation}}{{.Documentation}} |
| //{{end}} |
| // Asm: {{.Asm}}, CPU Feature: {{.CPUFeature}} |
| func ({{.Op1NameAndType "x"}}) {{.Go}}({{.Op0NameAndType "y"}}, {{.Op2NameAndType "z"}}) {{.GoType}} |
| {{end}} |
| |
| {{define "op3_231Type1"}} |
| {{if .Documentation}}{{.Documentation}} |
| //{{end}} |
| // Asm: {{.Asm}}, CPU Feature: {{.CPUFeature}} |
| func ({{.Op1NameAndType "x"}}) {{.Go}}({{.Op2NameAndType "y"}}, {{.Op0NameAndType "z"}}) {{.GoType}} |
| {{end}} |
| |
| {{define "op2VecAsScalar"}} |
| {{if .Documentation}}{{.Documentation}} |
| //{{end}} |
| // Asm: {{.Asm}}, CPU Feature: {{.CPUFeature}} |
| func ({{.Op0NameAndType "x"}}) {{.Go}}(y uint{{(index .In 1).TreatLikeAScalarOfSize}}) {{(index .Out 0).Go}} |
| {{end}} |
| |
| {{define "op3VecAsScalar"}} |
| {{if .Documentation}}{{.Documentation}} |
| //{{end}} |
| // Asm: {{.Asm}}, CPU Feature: {{.CPUFeature}} |
| func ({{.Op0NameAndType "x"}}) {{.Go}}(y uint{{(index .In 1).TreatLikeAScalarOfSize}}, {{.Op2NameAndType "z"}}) {{(index .Out 0).Go}} |
| {{end}} |
| |
| {{define "op4"}} |
| {{if .Documentation}}{{.Documentation}} |
| //{{end}} |
| // Asm: {{.Asm}}, CPU Feature: {{.CPUFeature}} |
| func ({{.Op0NameAndType "x"}}) {{.Go}}({{.Op1NameAndType "y"}}, {{.Op2NameAndType "z"}}, {{.Op3NameAndType "u"}}) {{.GoType}} |
| {{end}} |
| |
| {{define "op4_231Type1"}} |
| {{if .Documentation}}{{.Documentation}} |
| //{{end}} |
| // Asm: {{.Asm}}, CPU Feature: {{.CPUFeature}} |
| func ({{.Op1NameAndType "x"}}) {{.Go}}({{.Op2NameAndType "y"}}, {{.Op0NameAndType "z"}}, {{.Op3NameAndType "u"}}) {{.GoType}} |
| {{end}} |
| |
| {{define "op4_31"}} |
| {{if .Documentation}}{{.Documentation}} |
| //{{end}} |
| // Asm: {{.Asm}}, CPU Feature: {{.CPUFeature}} |
| func ({{.Op2NameAndType "x"}}) {{.Go}}({{.Op1NameAndType "y"}}, {{.Op0NameAndType "z"}}, {{.Op3NameAndType "u"}}) {{.GoType}} |
| {{end}} |
| |
| {{define "op1Imm8"}} |
| {{if .Documentation}}{{.Documentation}} |
| //{{end}} |
| // {{.ImmName}} is expected to be a constant, non-constant value will trigger a runtime panic. |
| // |
| // Asm: {{.Asm}}, CPU Feature: {{.CPUFeature}} |
| func ({{.Op1NameAndType "x"}}) {{.Go}}({{.ImmName}} uint8) {{.GoType}} |
| {{end}} |
| |
| {{define "op2Imm8"}} |
| {{if .Documentation}}{{.Documentation}} |
| //{{end}} |
| // {{.ImmName}} is expected to be a constant, non-constant value will trigger a runtime panic. |
| // |
| // Asm: {{.Asm}}, CPU Feature: {{.CPUFeature}} |
| func ({{.Op1NameAndType "x"}}) {{.Go}}({{.ImmName}} uint8, {{.Op2NameAndType "y"}}) {{.GoType}} |
| {{end}} |
| |
| {{define "op2Imm8_2I"}} |
| {{if .Documentation}}{{.Documentation}} |
| //{{end}} |
| // {{.ImmName}} is expected to be a constant, non-constant value will trigger a runtime panic. |
| // |
| // Asm: {{.Asm}}, CPU Feature: {{.CPUFeature}} |
| func ({{.Op1NameAndType "x"}}) {{.Go}}({{.Op2NameAndType "y"}}, {{.ImmName}} uint8) {{.GoType}} |
| {{end}} |
| |
| |
| {{define "op3Imm8"}} |
| {{if .Documentation}}{{.Documentation}} |
| //{{end}} |
| // {{.ImmName}} is expected to be a constant, non-constant value will trigger a runtime panic. |
| // |
| // Asm: {{.Asm}}, CPU Feature: {{.CPUFeature}} |
| func ({{.Op1NameAndType "x"}}) {{.Go}}({{.ImmName}} uint8, {{.Op2NameAndType "y"}}, {{.Op3NameAndType "z"}}) {{.GoType}} |
| {{end}} |
| |
| {{define "op3Imm8_2I"}} |
| {{if .Documentation}}{{.Documentation}} |
| //{{end}} |
| // {{.ImmName}} is expected to be a constant, non-constant value will trigger a runtime panic. |
| // |
| // Asm: {{.Asm}}, CPU Feature: {{.CPUFeature}} |
| func ({{.Op1NameAndType "x"}}) {{.Go}}({{.Op2NameAndType "y"}}, {{.ImmName}} uint8, {{.Op3NameAndType "z"}}) {{.GoType}} |
| {{end}} |
| |
| |
| {{define "op4Imm8"}} |
| {{if .Documentation}}{{.Documentation}} |
| //{{end}} |
| // {{.ImmName}} is expected to be a constant, non-constant value will trigger a runtime panic. |
| // |
| // Asm: {{.Asm}}, CPU Feature: {{.CPUFeature}} |
| func ({{.Op1NameAndType "x"}}) {{.Go}}({{.ImmName}} uint8, {{.Op2NameAndType "y"}}, {{.Op3NameAndType "z"}}, {{.Op4NameAndType "u"}}) {{.GoType}} |
| {{end}} |
| |
| {{define "vectorConversion"}} |
| // {{.Tdst.Name}} converts from {{.Tsrc.Name}} to {{.Tdst.Name}} |
| func (from {{.Tsrc.Name}}) As{{.Tdst.Name}}() (to {{.Tdst.Name}}) |
| {{end}} |
| |
| {{define "mask"}} |
| // converts from {{.Name}} to {{.VectorCounterpart}} |
| func (from {{.Name}}) As{{.VectorCounterpart}}() (to {{.VectorCounterpart}}) |
| |
| // converts from {{.VectorCounterpart}} to {{.Name}} |
| func (from {{.VectorCounterpart}}) As{{.Name}}() (to {{.Name}}) |
| |
| func (x {{.Name}}) And(y {{.Name}}) {{.Name}} |
| |
| func (x {{.Name}}) Or(y {{.Name}}) {{.Name}} |
| {{end}} |
| ` |
| |
| const simdTestsWrapperTmpl = `{{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_test |
| |
| import ( |
| "simd" |
| "testing" |
| ) |
| {{end}} |
| {{define "op"}} |
| func test{{.OpShape}}(t *testing.T, {{.BaseArgDefList}}, want []{{.ResBaseType}}, which string) { |
| t.Helper() |
| var gotv simd.{{.ResVecType}} |
| got := make([]{{.ResBaseType}}, len(want)){{range $i, $a := .ArgVecTypes}} |
| vec{{$i}} := simd.Load{{$a}}Slice(v{{$i}}){{end}} |
| switch which { |
| {{range .Ops}}case "{{.}}": |
| gotv = vec0.{{.}}({{$.VecArgList}}){{$.OptionalMaskToInt}} |
| {{end}} |
| default: |
| t.Errorf("Unknown method: {{.Arg0VecType}}.%s", which) |
| } |
| gotv.StoreSlice(got) |
| for i := range len(want) { |
| if got[i] != want[i] { |
| t.Errorf("Result at %d incorrect: want %v, got %v", i, want[i], got[i]) |
| } |
| } |
| } |
| {{end}} |
| {{define "untestedOpHeader"}} |
| /* The operations below cannot be tested via wrappers, please test them directly */ |
| {{end}} |
| {{define "untestedOp"}} |
| // {{.}}{{end}} |
| ` |
| |
| // writeSIMDTestsWrapper generates the test wrappers and writes it to simd_amd64_testwrappers.go |
| // within the specified directory. |
| func writeSIMDTestsWrapper(ops []Operation) *bytes.Buffer { |
| t := templateOf(simdTestsWrapperTmpl, "simdTestWrappers") |
| buffer := new(bytes.Buffer) |
| |
| if err := t.ExecuteTemplate(buffer, "fileHeader", nil); err != nil { |
| panic(fmt.Errorf("failed to execute fileHeader template: %w", err)) |
| } |
| |
| // The comment shows an example of Uint8x64.Add |
| type opData struct { |
| OpShape string // "Uint8x64Uint8x64Uint8x64" |
| BaseArgDefList string // "v0 uint8[], v1 uint8[]" |
| VecArgList string // "vec1" |
| ResBaseType string // "uint8" |
| ResVecType string // "Uint8x64" |
| Arg0VecType string // "Uint8x64" |
| ArgVecTypes []string // ["Uint8x64", "Uint8x64"] |
| OptionalMaskToInt string // ".AsInt8x64()" or "" |
| Ops []string // ["Add", "Sub"] |
| } |
| |
| opsByShape := make(map[string]opData) |
| opsSkipped := map[string]struct{}{} |
| outerLoop: |
| for _, o := range ops { |
| _, _, _, immType, gOp := o.shape() |
| |
| if immType == VarImm || immType == ConstVarImm { |
| // Operations with variable immediates should be called directly |
| // instead of through wrappers. |
| opsSkipped[o.Go] = struct{}{} |
| continue |
| } |
| if vasIdx, err := checkVecAsScalar(o); err != nil { |
| panic(err) |
| } else if vasIdx != -1 { |
| // TODO: these could be tested via wrappers, implement this. |
| opsSkipped[o.Go] = struct{}{} |
| continue |
| } |
| if o.OperandOrder != nil { |
| // We need to check if the customize order change the function signature. |
| // It is only safe to proceed generating the test wrappers if the function |
| // signature stays the same. |
| // Filtering out unqualified cases as a hack now, this test wrapper |
| // infrastrcuture should be changing soon so it should be fine. |
| switch *o.OperandOrder { |
| case "21": |
| // No op because it's only set in AndNot, and opr[2] and opr[1] has the same shape |
| default: |
| opsSkipped[o.Go] = struct{}{} |
| continue outerLoop |
| } |
| } |
| |
| var shape string |
| var baseArgDefList []string |
| var vecArgList []string |
| var argVecTypes []string |
| var vec string |
| var vecOp Operand |
| allSameVec := true |
| masked := strings.HasSuffix(gOp.Go, "Masked") |
| skippedMaskCnt := 0 |
| vecCnt := 0 |
| for i, in := range gOp.In { |
| baseArgDefList = append(baseArgDefList, fmt.Sprintf("v%d []%s%d", i, *in.Base, *in.ElemBits)) |
| if i != 0 { |
| maskConversion := "" |
| if in.Class == "mask" { |
| maskConversion = fmt.Sprintf(".As%s()", *in.Go) |
| } |
| vecArgList = append(vecArgList, fmt.Sprintf("vec%d%s", i, maskConversion)) |
| } |
| // gOp will only have either mask or vreg operand, so the following check |
| // is sufficient to detect whether it's a pure vreg or masked pure vreg operation |
| // with all the same vectors. |
| if in.Class == "mask" { |
| if masked && skippedMaskCnt == 0 { |
| skippedMaskCnt++ |
| } else { |
| allSameVec = false |
| } |
| } else { |
| if len(vec) > 0 { |
| if vec != *in.Go { |
| allSameVec = false |
| } |
| } |
| vecCnt++ |
| vec = *in.Go |
| vecOp = in |
| } |
| shape += *in.Go |
| argVecTypes = append(argVecTypes, strings.ReplaceAll(*in.Go, "Mask", "Int")) |
| } |
| isCompare := false |
| isWiden := false |
| outOp := gOp.Out[0] |
| if *outOp.Go != vec { |
| if allSameVec && outOp.Class == "mask" && *outOp.Bits == *vecOp.Bits && *outOp.Lanes == *vecOp.Lanes { |
| isCompare = true |
| } |
| if allSameVec && outOp.Class == "vreg" && *outOp.Bits == *vecOp.Bits && *outOp.Base == *vecOp.Base && *outOp.Lanes == *vecOp.Lanes/2 { |
| isWiden = true |
| } |
| if !isCompare && !isWiden { |
| allSameVec = false |
| } |
| } |
| shape += *gOp.Out[0].Go |
| if allSameVec { |
| numToName := map[int]string{1: "Unary", 2: "Binary", 3: "Ternary"} |
| if _, ok := numToName[vecCnt]; !ok { |
| panic(fmt.Errorf("unknown shape: %s", shape)) |
| } |
| shape = vec + numToName[vecCnt] |
| if masked { |
| shape += "Masked" |
| } |
| if isCompare { |
| if vecCnt == 2 { |
| // Remove "Binary" |
| shape = strings.ReplaceAll(shape, "Binary", "") |
| } |
| shape += "Compare" |
| } |
| if isWiden { |
| shape += "Widen" |
| } |
| } |
| optionalMaskToInt := "" |
| if gOp.Out[0].Class == "mask" { |
| optionalMaskToInt = fmt.Sprintf(".As%s()", strings.ReplaceAll(*gOp.Out[0].Go, "Mask", "Int")) |
| } |
| if _, ok := opsByShape[shape]; !ok { |
| opsByShape[shape] = opData{ |
| OpShape: shape, |
| BaseArgDefList: strings.Join(baseArgDefList, ", "), |
| VecArgList: strings.Join(vecArgList, ", "), |
| ResBaseType: fmt.Sprintf("%s%d", *gOp.Out[0].Base, *gOp.Out[0].ElemBits), |
| ResVecType: strings.ReplaceAll(*gOp.Out[0].Go, "Mask", "Int"), |
| Arg0VecType: *gOp.In[0].Go, |
| ArgVecTypes: argVecTypes, |
| OptionalMaskToInt: optionalMaskToInt, |
| } |
| } |
| data := opsByShape[shape] |
| data.Ops = append(data.Ops, gOp.Go) |
| opsByShape[shape] = data |
| } |
| |
| compareOpData := func(x, y opData) int { |
| return compareNatural(x.OpShape, y.OpShape) |
| } |
| data := make([]opData, 0) |
| for _, d := range opsByShape { |
| slices.SortFunc(d.Ops, compareNatural) |
| data = append(data, d) |
| } |
| slices.SortFunc(data, compareOpData) |
| |
| for _, d := range data { |
| if err := t.ExecuteTemplate(buffer, "op", d); err != nil { |
| panic(fmt.Errorf("failed to execute op template for op shape %s: %w", d.OpShape, err)) |
| } |
| } |
| |
| if len(opsSkipped) != 0 { |
| if err := t.ExecuteTemplate(buffer, "untestedOpHeader", nil); err != nil { |
| panic(fmt.Errorf("failed to execute untestedOpHeader")) |
| } |
| opsK := []string{} |
| for k := range opsSkipped { |
| opsK = append(opsK, k) |
| } |
| slices.SortFunc(opsK, strings.Compare) |
| for _, k := range opsK { |
| if err := t.ExecuteTemplate(buffer, "untestedOp", k); err != nil { |
| panic(fmt.Errorf("failed to execute untestedOp")) |
| } |
| } |
| } |
| |
| return buffer |
| } |
| |
| // parseSIMDTypes groups go simd types by their vector sizes, and |
| // returns a map whose key is the vector size, value is the simd type. |
| func parseSIMDTypes(ops []Operation) simdTypeMap { |
| // TODO: maybe instead of going over ops, let's try go over types.yaml. |
| ret := map[int][]simdType{} |
| seen := map[string]struct{}{} |
| processArg := func(arg Operand) { |
| if arg.Class == "immediate" || arg.Class == "greg" { |
| // Immediates are not encoded as vector types. |
| return |
| } |
| if _, ok := seen[*arg.Go]; ok { |
| return |
| } |
| seen[*arg.Go] = struct{}{} |
| |
| lanes := *arg.Lanes |
| base := fmt.Sprintf("%s%d", *arg.Base, *arg.ElemBits) |
| tagFieldNameS := fmt.Sprintf("%sx%d", base, lanes) |
| tagFieldS := fmt.Sprintf("%s v%d", tagFieldNameS, *arg.Bits) |
| valFieldS := fmt.Sprintf("vals%s[%d]%s", strings.Repeat(" ", len(tagFieldNameS)-3), lanes, base) |
| fields := fmt.Sprintf("\t%s\n\t%s", tagFieldS, valFieldS) |
| if arg.Class == "mask" { |
| vectorCounterpart := strings.ReplaceAll(*arg.Go, "Mask", "Int") |
| reshapedVectorWithAndOr := fmt.Sprintf("Int32x%d", *arg.Bits/32) |
| ret[*arg.Bits] = append(ret[*arg.Bits], simdType{*arg.Go, lanes, base, fields, arg.Class, vectorCounterpart, reshapedVectorWithAndOr, *arg.Bits}) |
| // In case the vector counterpart of a mask is not present, put its vector counterpart typedef into the map as well. |
| if _, ok := seen[vectorCounterpart]; !ok { |
| seen[vectorCounterpart] = struct{}{} |
| ret[*arg.Bits] = append(ret[*arg.Bits], simdType{vectorCounterpart, lanes, base, fields, "vreg", "", "", *arg.Bits}) |
| } |
| } else { |
| ret[*arg.Bits] = append(ret[*arg.Bits], simdType{*arg.Go, lanes, base, fields, arg.Class, "", "", *arg.Bits}) |
| } |
| } |
| for _, op := range ops { |
| for _, arg := range op.In { |
| processArg(arg) |
| } |
| for _, arg := range op.Out { |
| processArg(arg) |
| } |
| } |
| return ret |
| } |
| |
| func vConvertFromTypeMap(typeMap simdTypeMap) []simdTypePair { |
| v := []simdTypePair{} |
| for _, ts := range typeMap { |
| for i, tsrc := range ts { |
| for j, tdst := range ts { |
| if i != j && tsrc.Type == tdst.Type && tsrc.Type == "vreg" && |
| tsrc.Lanes > 1 && tdst.Lanes > 1 { |
| v = append(v, simdTypePair{tsrc, tdst}) |
| } |
| } |
| } |
| } |
| slices.SortFunc(v, compareSimdTypePairs) |
| return v |
| } |
| |
| func masksFromTypeMap(typeMap simdTypeMap) []simdType { |
| m := []simdType{} |
| for _, ts := range typeMap { |
| for _, tsrc := range ts { |
| if tsrc.Type == "mask" { |
| m = append(m, tsrc) |
| } |
| } |
| } |
| slices.SortFunc(m, compareSimdTypes) |
| return m |
| } |
| |
| func typesFromTypeMap(typeMap simdTypeMap) []simdType { |
| m := []simdType{} |
| for _, ts := range typeMap { |
| for _, tsrc := range ts { |
| if tsrc.Lanes > 1 { |
| m = append(m, tsrc) |
| } |
| } |
| } |
| slices.SortFunc(m, compareSimdTypes) |
| return m |
| } |
| |
| // writeSIMDTypes generates the simd vector types into a bytes.Buffer |
| func writeSIMDTypes(typeMap simdTypeMap) *bytes.Buffer { |
| t := templateOf(simdTypesTemplates, "types_amd64") |
| buffer := new(bytes.Buffer) |
| |
| if err := t.ExecuteTemplate(buffer, "fileHeader", nil); err != nil { |
| panic(fmt.Errorf("failed to execute fileHeader template: %w", err)) |
| } |
| |
| sizes := make([]int, 0, len(typeMap)) |
| for size, types := range typeMap { |
| slices.SortFunc(types, compareSimdTypes) |
| sizes = append(sizes, size) |
| } |
| sort.Ints(sizes) |
| |
| for _, size := range sizes { |
| if size <= 64 { |
| // these are scalar |
| continue |
| } |
| if err := t.ExecuteTemplate(buffer, "sizeTmpl", size); err != nil { |
| panic(fmt.Errorf("failed to execute size template for size %d: %w", size, err)) |
| } |
| for _, typeDef := range typeMap[size] { |
| if typeDef.Lanes == 1 { |
| continue |
| } |
| if err := t.ExecuteTemplate(buffer, "typeTmpl", typeDef); err != nil { |
| panic(fmt.Errorf("failed to execute type template for type %s: %w", typeDef.Name, err)) |
| } |
| } |
| } |
| |
| return buffer |
| } |
| |
| // writeSIMDStubs generates the simd vector intrinsic stubs and writes it to stubs_amd64.go |
| // within the specified directory. |
| func writeSIMDStubs(ops []Operation, typeMap simdTypeMap) *bytes.Buffer { |
| t := templateOf(simdStubsTmpl, "simdStubs") |
| buffer := new(bytes.Buffer) |
| |
| if err := t.ExecuteTemplate(buffer, "fileHeader", nil); err != nil { |
| panic(fmt.Errorf("failed to execute fileHeader template: %w", err)) |
| } |
| |
| slices.SortFunc(ops, compareOperations) |
| |
| for i, op := range ops { |
| idxVecAsScalar, err := checkVecAsScalar(op) |
| if err != nil { |
| panic(err) |
| } |
| if s, op, err := classifyOp(op); err == nil { |
| if idxVecAsScalar != -1 { |
| if s == "op2" || s == "op3" { |
| s += "VecAsScalar" |
| } else { |
| panic(fmt.Errorf("simdgen only supports op2 or op3 with TreatLikeAScalarOfSize")) |
| } |
| } |
| if i == 0 || op.Go != ops[i-1].Go { |
| fmt.Fprintf(buffer, "\n/* %s */\n", op.Go) |
| } |
| if err := t.ExecuteTemplate(buffer, s, op); err != nil { |
| panic(fmt.Errorf("failed to execute template %s for op %v: %w", s, op, err)) |
| } |
| |
| } else { |
| panic(fmt.Errorf("failed to classify op %v: %w", op.Go, err)) |
| } |
| } |
| |
| vectorConversions := vConvertFromTypeMap(typeMap) |
| for _, conv := range vectorConversions { |
| if err := t.ExecuteTemplate(buffer, "vectorConversion", conv); err != nil { |
| panic(fmt.Errorf("failed to execute vectorConversion template: %w", err)) |
| } |
| } |
| |
| masks := masksFromTypeMap(typeMap) |
| for _, mask := range masks { |
| if err := t.ExecuteTemplate(buffer, "mask", mask); err != nil { |
| panic(fmt.Errorf("failed to execute mask template for mask %s: %w", mask.Name, err)) |
| } |
| } |
| |
| return buffer |
| } |