1
2
3
4
5
6
7
8
9
10
11
12
13
14 package sgutil
15
16 import (
17 "bufio"
18 "bytes"
19 "fmt"
20 "os"
21 "regexp"
22 "slices"
23 "sort"
24 "strconv"
25 "strings"
26 "text/template"
27 )
28
29 const simdGenericOpsTmpl = `
30 package main
31
32 func simdGenericOps() []opData {
33 return []opData{
34 {{- range .Ops }}
35 {name: "{{.OpName}}", argLength: {{.OpInLen}}{{if .Comm}}, commutative: true{{end}}}, // ARCH:{{.ArchTag}}
36 {{- end }}
37 {{- range .OpsImm }}
38 {name: "{{.OpName}}", argLength: {{.OpInLen}}{{if .Comm}}, commutative: true{{end}}, aux: "UInt8"}, // ARCH:{{.ArchTag}}
39 {{- end }}
40 }
41 }
42 `
43
44
45 func TemplateNamed(name, temp string) *template.Template {
46 t, err := template.New(name).Parse(temp)
47 if err != nil {
48 panic(fmt.Errorf("failed to parse template %s: %w", name, err))
49 }
50 return t
51 }
52
53
54 const simdGenericOpsHeader = "// Code generated by 'simdgen' (merged automatically); DO NOT EDIT.\n"
55
56
57 type GenericOpsData struct {
58 OpName string
59 OpInLen int
60 Comm bool
61 HasAux bool
62 Archs []string
63 }
64
65
66 func (d GenericOpsData) ArchTag() string {
67 return strings.Join(d.Archs, ",")
68 }
69
70
71
72
73
74 var reEntry = regexp.MustCompile(`^\s*\{name:\s*"([^"]+)",\s*argLength:\s*(\d+)(?:,\s*commutative:\s*(true|false))?(?:,\s*aux:\s*"[^"]+")?\s*\}\s*,\s*(?://\s*ARCH:(\S+))?`)
75
76
77
78
79 func parseOps(oldFile, currentArch string) ([]GenericOpsData, error) {
80 f, ferr := os.Open(oldFile)
81 if ferr != nil {
82 if os.IsNotExist(ferr) {
83 return nil, nil
84 }
85 return nil, ferr
86 }
87 defer f.Close()
88
89 untagged := 0
90 unmatched := 0
91 noarch := 0
92
93 var result []GenericOpsData
94 scanner := bufio.NewScanner(f)
95 for scanner.Scan() {
96 line := scanner.Text()
97 matches := reEntry.FindStringSubmatch(line)
98 if matches == nil {
99 unmatched++
100 continue
101 }
102 name := matches[1]
103 argLen, _ := strconv.Atoi(matches[2])
104 comm := matches[3] == "true"
105 hasAux := strings.Contains(line, `aux: "UInt8"`)
106 archTag := matches[4]
107
108 if archTag == "" {
109 untagged++
110
111 continue
112 }
113
114 archs := slices.DeleteFunc(strings.Split(archTag, ","), func(a string) bool { return a == currentArch })
115 if len(archs) == 0 {
116 noarch++
117 continue
118 }
119
120 result = append(result, GenericOpsData{
121 OpName: name,
122 OpInLen: argLen,
123 Comm: comm,
124 HasAux: hasAux,
125 Archs: archs,
126 })
127 }
128 if err := scanner.Err(); err != nil {
129 return nil, err
130 }
131
132 return result, nil
133 }
134
135
136
137
138
139
140
141
142
143
144 func mergeOps(currentArch string, existing, new []GenericOpsData) ([]GenericOpsData, error) {
145 var (
146 result []GenericOpsData
147 i, j int
148 )
149 for i < len(existing) || j < len(new) {
150 var cmp int
151 if i < len(existing) && j < len(new) {
152 cmp = CompareNatural(existing[i].OpName, new[j].OpName)
153 } else if i < len(existing) {
154 cmp = -1
155 } else {
156 cmp = 1
157 }
158
159 var entry GenericOpsData
160 switch cmp {
161 case -1:
162 entry = existing[i]
163 i++
164
165 case 1:
166 entry = new[j]
167 j++
168
169 case 0:
170 e, n := existing[i], new[j]
171 if e.OpInLen != n.OpInLen {
172 return nil, fmt.Errorf("simdgen: op %q has inconsistent argLength: existing=%d, new=%d", e.OpName, e.OpInLen, n.OpInLen)
173 }
174 if e.Comm != n.Comm {
175 return nil, fmt.Errorf("simdgen: op %q has inconsistent commutativity: existing=%v, new=%v", e.OpName, e.Comm, n.Comm)
176 }
177 if e.HasAux != n.HasAux {
178 return nil, fmt.Errorf("simdgen: op %q has inconsistent HasAux: existing=%v, new=%v", e.OpName, e.HasAux, n.HasAux)
179 }
180 entry = e
181 entry.Archs = append(entry.Archs, currentArch)
182 sort.Strings(entry.Archs)
183 i++
184 j++
185 }
186 if len(result) > 0 && CompareNatural(result[len(result)-1].OpName, entry.OpName) >= 0 {
187 return nil, fmt.Errorf("simdgen: mergeOps: sort invariant violated between %q and %q",
188 result[len(result)-1].OpName, entry.OpName)
189 }
190 result = append(result, entry)
191 }
192
193 return result, nil
194 }
195
196
197
198 func MergeSIMDGenericOps(newOps []GenericOpsData, oldFile, currentArch string) *bytes.Buffer {
199 thisArch := []string{currentArch}
200 for i := range newOps {
201 newOps[i].Archs = thisArch
202 }
203
204 sortNatural := func(s []GenericOpsData) []GenericOpsData {
205 sort.Slice(s, func(i, j int) bool {
206 return CompareNatural(s[i].OpName, s[j].OpName) < 0
207 })
208 return s
209 }
210
211 existing, err := parseOps(oldFile, currentArch)
212 if err != nil {
213 panic(fmt.Errorf("failed to parse existing %s: %w", oldFile, err))
214 }
215
216 merged, err := mergeOps(currentArch, sortNatural(existing), sortNatural(newOps))
217 if err != nil {
218 panic(err)
219 }
220
221
222 type opData struct {
223 Ops []GenericOpsData
224 OpsImm []GenericOpsData
225 }
226 var opsData opData
227 for _, gOp := range merged {
228 if gOp.HasAux {
229 opsData.OpsImm = append(opsData.OpsImm, gOp)
230 } else {
231 opsData.Ops = append(opsData.Ops, gOp)
232 }
233 }
234
235 t := TemplateNamed("simdgenericOps", simdGenericOpsTmpl)
236 buffer := new(bytes.Buffer)
237 buffer.WriteString(simdGenericOpsHeader)
238
239 err = t.Execute(buffer, opsData)
240 if err != nil {
241 panic(fmt.Errorf("failed to execute template: %w", err))
242 }
243
244 return buffer
245 }
246
View as plain text