| // 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" |
| "strings" |
| ) |
| |
| const simdssaTmpl = `// 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 amd64 |
| |
| import ( |
| "cmd/compile/internal/ssa" |
| "cmd/compile/internal/ssagen" |
| "cmd/internal/obj" |
| "cmd/internal/obj/x86" |
| ) |
| |
| func ssaGenSIMDValue(s *ssagen.State, v *ssa.Value) bool { |
| p := s.Prog(v.Op.Asm()) |
| // First arg |
| switch v.Op {{"{"}}{{if gt (len .ImmFirst) 0}} |
| // Imm |
| case {{.ImmFirst}}: |
| imm := v.AuxInt |
| if imm < 0 || imm > 255 { |
| v.Fatalf("Invalid source selection immediate") |
| } |
| p.From.Offset = imm |
| p.From.Type = obj.TYPE_CONST |
| {{end}}{{if gt (len .VregFirst) 0}} |
| // vreg |
| case {{.VregFirst}}: |
| p.From.Type = obj.TYPE_REG |
| p.From.Reg = simdReg(v.Args[0]) |
| {{end}} |
| default: |
| // At least one arg is required. |
| return false |
| } |
| |
| // Second arg |
| switch v.Op {{"{"}}{{if gt (len .VregSecond) 0}} |
| // vreg |
| case {{.VregSecond}}: |
| if p.From.Type == obj.TYPE_CONST { |
| p.AddRestSourceReg(simdReg(v.Args[0])) |
| } else { |
| p.AddRestSourceReg(simdReg(v.Args[1])) |
| }{{end}} |
| } |
| |
| // Third arg |
| switch v.Op {{"{"}}{{if gt (len .VregThird) 0}} |
| // vreg |
| case {{.VregThird}}: |
| if p.From.Type == obj.TYPE_CONST { |
| p.AddRestSourceReg(simdReg(v.Args[1])) |
| } else { |
| p.AddRestSourceReg(simdReg(v.Args[2])) |
| } |
| {{end}}{{if gt (len .MaskThird) 0}} |
| // k mask |
| case {{.MaskThird}}: |
| if p.From.Type == obj.TYPE_CONST { |
| p.AddRestSourceReg(v.Args[1].Reg()) |
| } else { |
| p.AddRestSourceReg(v.Args[2].Reg()) |
| }{{end}} |
| } |
| |
| // Fourth arg |
| switch v.Op {{"{"}}{{if gt (len .MaskFourth) 0}} |
| case {{.MaskFourth}}: |
| if p.From.Type == obj.TYPE_CONST { |
| p.AddRestSourceReg(v.Args[2].Reg()) |
| } else { |
| p.AddRestSourceReg(v.Args[3].Reg()) |
| }{{end}} |
| } |
| |
| // Output |
| switch v.Op {{"{"}}{{if gt (len .VregOut) 0}} |
| case {{.VregOut}}: |
| p.To.Type = obj.TYPE_REG |
| p.To.Reg = simdReg(v) |
| {{end}}{{if gt (len .MaskOut) 0}} |
| case {{.MaskOut}}: |
| p.To.Type = obj.TYPE_REG |
| p.To.Reg = v.Reg() |
| {{end}} |
| default: |
| // One result is required. |
| return false |
| } |
| {{if gt (len .ZeroingMask) 0}} |
| // Masked operation are always compiled with zeroing. |
| switch v.Op { |
| case {{.ZeroingMask}}: |
| x86.ParseSuffix(p, "Z") |
| } |
| {{end}} |
| return true |
| } |
| ` |
| |
| // writeSIMDSSA generates the ssa to prog lowering codes and writes it to simdssa.go |
| // within the specified directory. |
| func writeSIMDSSA(directory string, ops []Operation) error { |
| var ImmFirst []string |
| var VregFirst []string |
| var VregSecond []string |
| var MaskThird []string |
| var VregThird []string |
| var MaskFourth []string |
| var VregOut []string |
| var MaskOut []string |
| var ZeroingMask []string |
| |
| seen := map[string]struct{}{} |
| for _, op := range ops { |
| asm := op.Asm |
| shapeIn, shapeOut, maskType, _, _, gOp, err := op.shape() |
| if err != nil { |
| return err |
| } |
| if maskType == 2 { |
| asm += "Masked" |
| } |
| asm = fmt.Sprintf("%s%d", asm, *gOp.Out[0].Bits) |
| if _, ok := seen[asm]; ok { |
| continue |
| } |
| seen[asm] = struct{}{} |
| caseStr := fmt.Sprintf("ssa.OpAMD64%s", asm) |
| if shapeIn == PureVregIn || shapeIn == PureKmaskIn { |
| // Masks and vreg are handled together by simdReg() |
| VregFirst = append(VregFirst, caseStr) |
| if len(gOp.In) > 1 { |
| VregSecond = append(VregSecond, caseStr) |
| } |
| } else if shapeIn == OneKmaskIn { |
| VregFirst = append(VregFirst, caseStr) |
| VregSecond = append(VregSecond, caseStr) |
| MaskThird = append(MaskThird, caseStr) |
| if gOp.Zeroing == nil { |
| ZeroingMask = append(ZeroingMask, caseStr) |
| } |
| } else if shapeIn == OneConstImmIn { |
| ImmFirst = append(ImmFirst, caseStr) |
| VregSecond = append(VregSecond, caseStr) |
| VregThird = append(VregThird, caseStr) |
| } else { |
| // OneKmaskConstImmIn case |
| ImmFirst = append(ImmFirst, caseStr) |
| VregSecond = append(VregSecond, caseStr) |
| VregThird = append(VregThird, caseStr) |
| MaskFourth = append(MaskFourth, caseStr) |
| if gOp.Zeroing == nil { |
| ZeroingMask = append(ZeroingMask, caseStr) |
| } |
| } |
| if shapeOut == OneVregOut || gOp.Out[0].OverwriteClass != nil { |
| // If class overwrite is happening, that's not really a mask but a vreg. |
| VregOut = append(VregOut, caseStr) |
| } else { |
| // OneKmaskOut case |
| MaskOut = append(MaskOut, caseStr) |
| } |
| } |
| |
| data := struct { |
| ImmFirst string |
| VregFirst string |
| VregSecond string |
| MaskThird string |
| VregThird string |
| MaskFourth string |
| VregOut string |
| MaskOut string |
| ZeroingMask string |
| }{ |
| strings.Join(ImmFirst, ", "), |
| strings.Join(VregFirst, ", "), |
| strings.Join(VregSecond, ", "), |
| strings.Join(MaskThird, ", "), |
| strings.Join(VregThird, ", "), |
| strings.Join(MaskFourth, ", "), |
| strings.Join(VregOut, ", "), |
| strings.Join(MaskOut, ", "), |
| strings.Join(ZeroingMask, ", "), |
| } |
| |
| file, t, err := openFileAndPrepareTemplate(directory, "src/cmd/compile/internal/amd64/simdssa.go", simdssaTmpl) |
| if err != nil { |
| return err |
| } |
| defer file.Close() |
| |
| err = t.Execute(file, data) |
| if err != nil { |
| return fmt.Errorf("failed to execute template: %w", err) |
| } |
| |
| return nil |
| } |