blob: 0deec9c640bf81803d89fee4127805ccc9c0f5bc [file] [log] [blame]
// 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 (
"fmt"
"sort"
)
const simdMachineOpsTmpl = `// 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.
package main
func simdAMD64Ops(fp1fp1, fp2fp1, fp2m1, fp2m1fp1, fp2m1m1 regInfo) []opData {
return []opData{
{{- range .OpsData }}
{name: "{{.OpName}}", argLength: {{.OpInLen}}, reg: {{.RegInfo}}, asm: "{{.Asm}}", commutative: {{.Comm}}, typ: "{{.Type}}"},
{{- end }}
{{- range .OpsDataImm }}
{name: "{{.OpName}}", argLength: {{.OpInLen}}, reg: {{.RegInfo}}, asm: "{{.Asm}}", aux: "Int8", commutative: {{.Comm}}, typ: "{{.Type}}"},
{{- end }}
}
}
`
// writeSIMDMachineOps generates the machine ops and writes it to simdAMD64ops.go
// within the specified directory.
func writeSIMDMachineOps(directory string, ops []Operation) error {
file, t, err := openFileAndPrepareTemplate(directory, "src/cmd/compile/internal/ssa/_gen/simdAMD64ops.go", simdMachineOpsTmpl)
if err != nil {
return err
}
defer file.Close()
type opData struct {
sortKey string
OpName string
Asm string
OpInLen int
RegInfo string
Comm string
Type string
}
type machineOpsData struct {
OpsData []opData
OpsDataImm []opData
}
seen := map[string]struct{}{}
regInfoSet := map[string]bool{"fp1fp1": true, "fp2fp1": true, "fp2m1": true, "fp2m1fp1": true, "fp2m1m1": true}
opsData := make([]opData, 0)
opsDataImm := make([]opData, 0)
for _, op := range ops {
shapeIn, shapeOut, maskType, _, _, gOp, err := op.shape()
if err != nil {
return err
}
asm := gOp.Asm
if maskType == OneMask {
asm += "Masked"
}
asm = fmt.Sprintf("%s%d", asm, *gOp.Out[0].Bits)
// TODO: all our masked operations are now zeroing, we need to generate machine ops with merging masks, maybe copy
// one here with a name suffix "Merging". The rewrite rules will need them.
if _, ok := seen[asm]; ok {
continue
}
seen[asm] = struct{}{}
var regInfo string
// Process input reg shapes.
var vRegInCnt, kMaskInCnt, vRegOutCnt, kMaskOutCnt int
for _, in := range gOp.In {
if in.Class == "vreg" {
vRegInCnt++
} else if in.Class == "mask" {
kMaskInCnt++
}
}
for _, out := range gOp.Out {
// If class overwrite is happening, that's not really a mask but a vreg.
if out.Class == "vreg" || out.OverwriteClass != nil {
vRegOutCnt++
} else if out.Class == "mask" {
kMaskOutCnt++
}
}
var vRegInS, kMaskInS, vRegOutS, kMaskOutS string
if vRegInCnt > 0 {
vRegInS = fmt.Sprintf("fp%d", vRegInCnt)
}
if kMaskInCnt > 0 {
kMaskInS = fmt.Sprintf("m%d", kMaskInCnt)
}
if vRegOutCnt > 0 {
vRegOutS = fmt.Sprintf("fp%d", vRegOutCnt)
}
if kMaskOutCnt > 0 {
kMaskOutS = fmt.Sprintf("m%d", kMaskOutCnt)
}
regInfo = fmt.Sprintf("%s%s%s%s", vRegInS, kMaskInS, vRegOutS, kMaskOutS)
if _, ok := regInfoSet[regInfo]; !ok {
return fmt.Errorf("unsupported register constraint, please update the template and AMD64Ops.go: %s", regInfo)
}
var outType string
if shapeOut == OneVregOut || gOp.Out[0].OverwriteClass != nil {
// If class overwrite is happening, that's not really a mask but a vreg.
outType = fmt.Sprintf("Vec%d", *gOp.Out[0].Bits)
} else if shapeOut == OneKmaskOut {
outType = "Mask"
} else {
return fmt.Errorf("simdgen does not recognize this output shape: %+v", shapeOut)
}
if shapeIn == OneConstImmIn || shapeIn == OneKmaskConstImmIn {
opsDataImm = append(opsDataImm, opData{*gOp.In[0].Go + gOp.Go, asm, gOp.Asm, len(gOp.In), regInfo, gOp.Commutative, outType})
} else {
opsData = append(opsData, opData{*gOp.In[0].Go + gOp.Go, asm, gOp.Asm, len(gOp.In), regInfo, gOp.Commutative, outType})
}
}
sort.Slice(opsData, func(i, j int) bool {
return opsData[i].sortKey < opsData[j].sortKey
})
sort.Slice(opsDataImm, func(i, j int) bool {
return opsDataImm[i].sortKey < opsDataImm[j].sortKey
})
err = t.Execute(file, machineOpsData{opsData, opsDataImm})
if err != nil {
return fmt.Errorf("failed to execute template: %w", err)
}
return nil
}