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