1
2
3
4
5 package sve
6
7 import (
8 "fmt"
9 "log"
10 "regexp"
11 "slices"
12 "strings"
13
14 "simd/archsimd/_gen/simdgen/types"
15 "simd/archsimd/_gen/unify"
16 )
17
18 var baseTypeRegexps = map[string]*regexp.Regexp{
19 "int": regexp.MustCompile("int"),
20 "uint": regexp.MustCompile("uint"),
21 "float": regexp.MustCompile("float"),
22 }
23
24
25 func asComment(text string, width int) string {
26 text = strings.TrimSpace(text)
27 text = strings.ReplaceAll(text, "&", "&")
28 text = strings.ReplaceAll(text, "\n", " ")
29 words := strings.Fields(text)
30 var lines []string
31 line := ""
32 for _, w := range words {
33 if line != "" {
34 line += " "
35 }
36 line += w
37 if len(line) >= width {
38 lines = append(lines, "// "+line)
39 line = ""
40 }
41 }
42 if line != "" {
43 lines = append(lines, "// "+line)
44 }
45 return strings.Join(lines, "\n")
46 }
47
48
49
50 var mixedWidthLogged = map[string]bool{}
51
52
53
54
55 func (op *Operand) encode() types.Operand {
56 out := types.Operand{
57 Class: op.Class,
58 AsmPos: op.AsmPos,
59 }
60 if op.BaseType != "" {
61 if re, ok := baseTypeRegexps[op.BaseType]; ok {
62 out.EncodeBase = re
63 } else {
64 out.EncodeBase = regexp.MustCompile(op.BaseType)
65 }
66 }
67 switch {
68 case op.Bits > 0:
69
70 out.EncodeBits = &types.VectorSize{NRaw: op.Bits}
71 if op.Lanes > 0 {
72 out.Lanes = new(op.Lanes)
73 }
74 case op.Class == "vreg" || op.Class == "mask":
75
76
77
78
79 out.EncodeBits = &types.VectorSize{Scalable: true}
80 }
81 if op.ElemBits > 0 {
82 out.ElemBits = new(op.ElemBits)
83 }
84 if op.Predication != "" {
85
86
87 out.Predication = new(op.Predication)
88 }
89 if op.governing {
90
91 out.Governing = new(true)
92 }
93 if op.isList {
94
95
96 out.ListNumber = new(0)
97 }
98 if op.regName != "" {
99
100 out.RegName = new(op.regName)
101 }
102
103
104
105
106
107
108
109
110 out.PredRegName = new(op.predRegName)
111 return out
112 }
113
114
115
116
117 func pickRegNames(variants []predVariant, idx int, sel func(predVariant) []string) []string {
118 if len(variants) == 0 {
119 return nil
120 }
121 out := make([]string, len(variants))
122 for i, pv := range variants {
123 names := sel(pv)
124 if idx >= len(names) {
125 panic(fmt.Sprintf("operand %d has no counterpart in predicated encoding %d", idx, i))
126 }
127 out[i] = names[idx]
128 }
129 return out
130 }
131
132
133
134
135
136
137
138 func (inst *Instruction) emitOne(asm string, ops []Operand, widthAgnostic bool) *unify.Value {
139 var db unify.DefBuilder
140 db.Add("asm", unify.NewValue(unify.NewStringExact(asm)))
141 db.Add("goarch", unify.NewValue(unify.NewStringExact("arm64")))
142
143
144
145
146 feature := inst.cpuFeature()
147 unpred := ""
148 for _, pv := range inst.predVariants {
149 if pv.cpuFeature == "SVE" && feature == "SVE2" {
150 unpred = feature
151 feature = pv.cpuFeature
152 }
153 }
154 db.Add("cpuFeature", unify.NewValue(unify.NewStringExact(feature)))
155 if unpred != "" {
156 db.Add("unpredCPUFeature", unify.NewValue(unify.NewStringExact(unpred)))
157 }
158 if doc := inst.documentation(); doc != "" {
159 db.Add("details", unify.NewValue(unify.NewStringExact(asComment(doc, 80))))
160 }
161 if widthAgnostic {
162 db.Add("widthAgnostic", unify.NewValue(unify.NewStringExact("true")))
163 }
164
165
166
167
168
169
170 var in, out []types.Operand
171 var outIdx, inIdx int
172 for _, op := range ops {
173 switch {
174 case op.governing:
175
176
177 in = append(in, op.encode())
178 case op.role == "destination":
179 op.predRegName = pickRegNames(inst.predVariants, outIdx, func(pv predVariant) []string { return pv.outRegNames })
180 outIdx++
181 out = append(out, op.encode())
182 default:
183 op.predRegName = pickRegNames(inst.predVariants, inIdx, func(pv predVariant) []string { return pv.inRegNames })
184 inIdx++
185 in = append(in, op.encode())
186 }
187 }
188 slices.SortStableFunc(in, types.Operand.Compare)
189
190 db.Add("in", unify.Encode(in))
191 var inVar []types.Operand
192 for _, pv := range inst.predVariants {
193
194 inVar = append(inVar, types.Operand{
195 Class: "mask",
196 Bits: types.VectorSize{Scalable: true},
197 Predication: new(pv.quals),
198 AsmPos: pv.predAsmPos,
199 })
200 }
201 db.Add("inVariant", unify.Encode(inVar))
202 db.Add("out", unify.Encode(out))
203 return unify.NewValue(db.Build())
204 }
205
206
207
208
209 func (inst *Instruction) emitAll() []*unify.Value {
210
211
212 defs, _, _ := inst.classify()
213 return defs
214 }
215
216
217 func lookup(rows []arngRow, size string) (int, bool) {
218 for _, r := range rows {
219 if r.size == size {
220 return r.bits, true
221 }
222 }
223 return 0, false
224 }
225
226
227
228
229
230
231
232
233
234 func (inst *Instruction) emitVariants(template []Operand) []*unify.Value {
235 asm := inst.goOpPrefix() + inst.mnemonic()
236
237 links := arngLinks(template)
238 tables := map[string][]arngRow{}
239 for _, l := range links {
240 tables[l] = inst.resolveArrangementTable(l)
241 }
242
243
244
245 var sizes []string
246 if len(links) > 0 {
247 for _, r := range tables[links[0]] {
248 sizes = append(sizes, r.size)
249 }
250 } else {
251 sizes = []string{""}
252 }
253
254 signs := inst.integerSignedness(template)
255
256
257
258 preds := predicationVariants(template)
259
260
261
262
263
264
265 widths := []int{0}
266 widthAgnostic := len(links) == 0 && inst.bitwise()
267 if widthAgnostic {
268 widths = []int{8, 16, 32, 64}
269 }
270
271 var defs []*unify.Value
272 for _, sign := range signs {
273 for _, size := range sizes {
274 ops := make([]Operand, len(template))
275 copy(ops, template)
276 skip := false
277 for i := range ops {
278 eb := ops[i].fixedElem
279 if ops[i].fixedBits > 0 {
280
281
282 eb = ops[i].fixedBits
283 } else if l := ops[i].arngLink; l != "" {
284 b, ok := lookup(tables[l], size)
285 if !ok {
286
287
288 skip = true
289 break
290 }
291 eb = b
292 }
293 base := sign
294 if inst.laneIsFloat(&ops[i]) {
295 base = "float"
296 if eb > 0 && eb < 16 {
297
298 skip = true
299 break
300 }
301 }
302 ops[i].instantiate(base, eb)
303 }
304 if skip {
305 continue
306 }
307 for _, pred := range preds {
308 variant := make([]Operand, len(ops))
309 copy(variant, ops)
310 elem := 0
311 mixedWidths := false
312 for i := range variant {
313 if variant[i].Class == "vreg" && variant[i].ElemBits > 0 {
314 if elem == 0 {
315 elem = variant[i].ElemBits
316 } else if variant[i].ElemBits != elem {
317 mixedWidths = true
318 }
319 }
320 }
321 for i := range variant {
322 if variant[i].Class != "mask" {
323 continue
324 }
325 if variant[i].governing {
326 variant[i].Predication = pred
327 }
328 if variant[i].ElemBits == 0 {
329
330
331 if mixedWidths && !mixedWidthLogged[inst.mnemonic()] {
332 mixedWidthLogged[inst.mnemonic()] = true
333 log.Printf("sve: %s: operands have mixed element widths; predicate width provisionally %d — derive esize from the pseudocode before generating an API from this def",
334 inst.mnemonic(), elem)
335 }
336 variant[i].ElemBits = elem
337 }
338 }
339 for _, w := range widths {
340 v := variant
341 if w > 0 {
342 v = make([]Operand, len(variant))
343 copy(v, variant)
344 for i := range v {
345 if v[i].Class == "vreg" || v[i].Class == "mask" {
346 v[i].ElemBits = w
347 }
348 }
349 }
350 defs = append(defs, inst.emitOne(asm, v, widthAgnostic))
351 }
352 }
353 }
354 }
355 return defs
356 }
357
View as plain text