1
2
3
4
5 package main
6
7 import (
8 "fmt"
9 "log"
10 "math/rand/v2"
11 "regexp"
12 "slices"
13 "strconv"
14 "strings"
15 "unicode"
16
17 "simd/archsimd/_gen/unify"
18 )
19
20 type Operation struct {
21 rawOperation
22
23
24
25
26
27 Go string
28
29
30
31
32
33
34
35
36 Documentation string
37
38
39
40
41 In []Operand
42 }
43
44
45
46 type rawOperation struct {
47 Go string
48
49 GoArch string
50 Asm string
51 Arrangement *string
52 OperandOrder *string
53
54
55 SpecialLower *string
56
57
58
59 HiHalfAsm *string
60
61 In []Operand
62 InVariant []Operand
63 Out []Operand
64 MemFeatures *string
65 MemFeaturesData *string
66 Commutative bool
67 CPUFeature string
68 Zeroing *bool
69 Documentation *string
70 AddDoc *string
71
72
73 ConstImm *string
74
75 NameAndSizeCheck *bool
76
77 NoTypes *string
78
79 NoGenericOps *string
80
81 SSAVariant *string
82
83
84 HideMaskMethods *bool
85 }
86
87 func (o *Operation) IsMasked() bool {
88 if len(o.InVariant) == 0 {
89 return false
90 }
91 if len(o.InVariant) == 1 && o.InVariant[0].Class == "mask" {
92 return true
93 }
94 panic(fmt.Errorf("unknown inVariant"))
95 }
96
97 func (o *Operation) SkipMaskedMethod() bool {
98 if o.HideMaskMethods == nil {
99 return false
100 }
101 if *o.HideMaskMethods && o.IsMasked() {
102 return true
103 }
104 return false
105 }
106
107
108
109 func (o *Operation) hiHalfKind() string {
110 if o.HiHalfAsm == nil {
111 return ""
112 }
113
114 var inElemBits, outElemBits *int
115 for i := range o.In {
116 if o.In[i].Class == "vreg" && o.In[i].ElemBits != nil {
117 inElemBits = o.In[i].ElemBits
118 break
119 }
120 }
121 for i := range o.Out {
122 if o.Out[i].Class == "vreg" && o.Out[i].ElemBits != nil {
123 outElemBits = o.Out[i].ElemBits
124 break
125 }
126 }
127 if inElemBits == nil || outElemBits == nil {
128 return ""
129 }
130 if *outElemBits < *inElemBits {
131 return "narrow"
132 }
133 if *outElemBits > *inElemBits {
134 return "long"
135 }
136 return ""
137 }
138
139 var reForName = regexp.MustCompile(`\bNAME\b`)
140
141 func (o *Operation) DecodeUnified(v *unify.Value) error {
142 if err := v.Decode(&o.rawOperation); err != nil {
143 return err
144 }
145
146 isMasked := o.IsMasked()
147
148
149 o.Go = o.rawOperation.Go
150 if isMasked {
151 o.Go += "Masked"
152 }
153
154
155 if o.rawOperation.Documentation != nil {
156 o.Documentation = *o.rawOperation.Documentation
157 } else {
158 o.Documentation = "// UNDOCUMENTED"
159 }
160 o.Documentation = reForName.ReplaceAllString(o.Documentation, o.Go)
161 if isMasked {
162 o.Documentation += "\n//\n// This operation is applied selectively under a write mask."
163
164 if unicode.IsUpper([]rune(o.Go)[0]) {
165 trueVal := "true"
166 o.NoGenericOps = &trueVal
167 o.NoTypes = &trueVal
168 }
169 }
170 if o.rawOperation.AddDoc != nil {
171 o.Documentation += "\n" + reForName.ReplaceAllString(*o.rawOperation.AddDoc, o.Go)
172 }
173
174 o.In = append(o.rawOperation.In, o.rawOperation.InVariant...)
175
176
177
178 if o.rawOperation.HiHalfAsm != nil && o.hiHalfKind() == "long" {
179
180 vregIns := 0
181 for _, in := range o.In {
182 if in.Class == "vreg" {
183 vregIns++
184 }
185 }
186
187 switch vregIns {
188 case 2:
189
190 o.Documentation += "\n// For the high-indexed elements, use HiToLo:\n//\n//\tx.HiToLo()." + o.Go + "(y.HiToLo())"
191 case 1:
192
193 o.Documentation += "\n// For the high-indexed elements, use HiToLo:\n//\n//\tx.HiToLo()." + o.Go + "(...)"
194 }
195 }
196
197
198
199 if len(o.In) > 0 && len(o.Out) > 0 {
200 inLanes := o.In[0].Lanes
201 outLanes := o.Out[0].Lanes
202 if inLanes != nil && outLanes != nil && *inLanes < *outLanes {
203 if (strings.Contains(o.Go, "Saturate") || strings.Contains(o.Go, "TruncTo")) &&
204 !strings.Contains(o.Go, "Concat") {
205 o.Documentation += "\n// Results are packed to low elements in the returned vector, its upper elements are zeroed."
206 }
207 }
208 }
209
210 return nil
211 }
212
213 func (o *Operation) VectorWidth() int {
214 out := o.Out[0]
215 if out.Class == "vreg" {
216 return *out.Bits
217 } else if out.Class == "greg" || out.Class == "mask" {
218 for i := range o.In {
219 if o.In[i].Class == "vreg" {
220 return *o.In[i].Bits
221 }
222 }
223 }
224 panic(fmt.Errorf("Figure out what the vector width is for %v and implement it", *o))
225 }
226
227
228
229
230
231
232
233
234
235 var demotingConvertOps = map[string]bool{
236 "VPMOVQD128": true, "VPMOVSQD128": true, "VPMOVUSQD128": true, "VPMOVQW128": true, "VPMOVSQW128": true,
237 "VPMOVUSQW128": true, "VPMOVDW128": true, "VPMOVSDW128": true, "VPMOVUSDW128": true, "VPMOVQB128": true,
238 "VPMOVSQB128": true, "VPMOVUSQB128": true, "VPMOVDB128": true, "VPMOVSDB128": true, "VPMOVUSDB128": true,
239 "VPMOVWB128": true, "VPMOVSWB128": true, "VPMOVUSWB128": true,
240 "VPMOVQDMasked128": true, "VPMOVSQDMasked128": true, "VPMOVUSQDMasked128": true, "VPMOVQWMasked128": true, "VPMOVSQWMasked128": true,
241 "VPMOVUSQWMasked128": true, "VPMOVDWMasked128": true, "VPMOVSDWMasked128": true, "VPMOVUSDWMasked128": true, "VPMOVQBMasked128": true,
242 "VPMOVSQBMasked128": true, "VPMOVUSQBMasked128": true, "VPMOVDBMasked128": true, "VPMOVSDBMasked128": true, "VPMOVUSDBMasked128": true,
243 "VPMOVWBMasked128": true, "VPMOVSWBMasked128": true, "VPMOVUSWBMasked128": true,
244 }
245
246 func machineOpName(maskType maskShape, gOp Operation) string {
247 asm := gOp.Asm
248 if maskType == OneMask {
249 asm += "Masked"
250 }
251
252 if gOp.Arrangement != nil && *gOp.Arrangement != "" {
253 asm = fmt.Sprintf("%s%s", asm, *gOp.Arrangement)
254 } else {
255 asm = fmt.Sprintf("%s%d", asm, gOp.VectorWidth())
256 }
257 if gOp.SSAVariant != nil {
258 asm += *gOp.SSAVariant
259 }
260 if demotingConvertOps[asm] {
261
262
263 asm = fmt.Sprintf("%s_%d", asm, *gOp.In[0].Bits)
264 }
265 return asm
266 }
267
268 func compareStringPointers(x, y *string) int {
269 if x != nil && y != nil {
270 return compareNatural(*x, *y)
271 }
272 if x == nil && y == nil {
273 return 0
274 }
275 if x == nil {
276 return -1
277 }
278 return 1
279 }
280
281 func compareIntPointers(x, y *int) int {
282 if x != nil && y != nil {
283 return *x - *y
284 }
285 if x == nil && y == nil {
286 return 0
287 }
288 if x == nil {
289 return -1
290 }
291 return 1
292 }
293
294 func compareOperations(x, y Operation) int {
295 if c := compareNatural(x.Go, y.Go); c != 0 {
296 return c
297 }
298 xIn, yIn := x.In, y.In
299
300 if len(xIn) > len(yIn) && xIn[len(xIn)-1].Class == "mask" {
301 xIn = xIn[:len(xIn)-1]
302 } else if len(xIn) < len(yIn) && yIn[len(yIn)-1].Class == "mask" {
303 yIn = yIn[:len(yIn)-1]
304 }
305
306 if len(xIn) < len(yIn) {
307 return -1
308 }
309 if len(xIn) > len(yIn) {
310 return 1
311 }
312 if len(x.Out) < len(y.Out) {
313 return -1
314 }
315 if len(x.Out) > len(y.Out) {
316 return 1
317 }
318 for i := range xIn {
319 ox, oy := &xIn[i], &yIn[i]
320 if c := compareOperands(ox, oy); c != 0 {
321 return c
322 }
323 }
324 return 0
325 }
326
327 func compareOperands(x, y *Operand) int {
328 if c := compareNatural(x.Class, y.Class); c != 0 {
329 return c
330 }
331 if x.Class == "immediate" {
332 return compareStringPointers(x.ImmOffset, y.ImmOffset)
333 } else {
334 if c := compareStringPointers(x.Base, y.Base); c != 0 {
335 return c
336 }
337 if c := compareIntPointers(x.ElemBits, y.ElemBits); c != 0 {
338 return c
339 }
340 if c := compareIntPointers(x.Bits, y.Bits); c != 0 {
341 return c
342 }
343 if c := compareIntPointers(x.ListNumber, y.ListNumber); c != 0 {
344 return c
345 }
346 return 0
347 }
348 }
349
350 type Operand struct {
351 Class string
352
353 Go *string
354 AsmPos int
355
356 Base *string
357 ElemBits *int
358 Bits *int
359
360 Const *string
361
362
363
364
365 ImmOffset *string
366 ImmMax *int
367 Name *string
368 Lanes *int
369
370
371
372 TreatLikeAScalarOfSize *int
373
374
375 OverwriteClass *string
376
377
378 OverwriteBase *string
379
380
381
382 OverwriteElementBits *int
383
384
385 OverwriteBits *int
386
387 FixedReg *string
388
389
390
391 ListNumber *int
392 }
393
394
395 func isDigit(b byte) bool {
396 return b >= '0' && b <= '9'
397 }
398
399
400
401
402
403
404
405
406
407
408
409 func compareNatural(s1, s2 string) int {
410 i, j := 0, 0
411 len1, len2 := len(s1), len(s2)
412
413 for i < len1 && j < len2 {
414
415 if isDigit(s1[i]) && isDigit(s2[j]) {
416
417 numStart1 := i
418 for i < len1 && isDigit(s1[i]) {
419 i++
420 }
421 num1, _ := strconv.Atoi(s1[numStart1:i])
422
423 numStart2 := j
424 for j < len2 && isDigit(s2[j]) {
425 j++
426 }
427 num2, _ := strconv.Atoi(s2[numStart2:j])
428
429 if num1 < num2 {
430 return -1
431 }
432 if num1 > num2 {
433 return 1
434 }
435
436 if ln1, ln2 := i-numStart1, j-numStart2; ln1 != ln2 {
437 return ln1 - ln2
438 }
439
440 } else {
441
442 if s1[i] < s2[j] {
443 return -1
444 }
445 if s1[i] > s2[j] {
446 return 1
447 }
448 i++
449 j++
450 }
451 }
452
453
454 return strings.Compare(s1, s2)
455 }
456
457
458 func generatedHeader() string {
459 return CurrentArch().GeneratedHeader
460 }
461
462 func writeGoDefs(path string, cl unify.Closure) error {
463
464
465 var ops []Operation
466 for def := range cl.All() {
467 var op Operation
468 if !def.Exact() {
469 continue
470 }
471 if err := def.Decode(&op); err != nil {
472 log.Println(err.Error())
473 log.Println(def)
474 continue
475 }
476
477 op.sortOperand()
478 op.adjustAsm()
479 ops = append(ops, op)
480 }
481
482 rand.Shuffle(len(ops), func(i, j int) {
483 ops[i], ops[j] = ops[j], ops[i]
484 })
485
486 slices.SortFunc(ops, compareOperations)
487
488
489 deduped := dedup(ops)
490 slices.SortFunc(deduped, compareOperations)
491
492 if *Verbose {
493 log.Printf("dedup len: %d, ops len: %d\n", len(deduped), len(ops))
494 }
495 var err error
496 if err = overwrite(deduped); err != nil {
497 return err
498 }
499 if *Verbose {
500 log.Printf("dedup len: %d\n", len(deduped))
501 }
502 if !*FlagNoDedup {
503
504
505 if deduped, err = dedupGodef(deduped); err != nil {
506 return err
507 }
508 }
509 if *Verbose {
510 log.Printf("dedup len: %d\n", len(deduped))
511 }
512 if !*FlagNoConstImmPorting {
513 if err = copyConstImm(deduped); err != nil {
514 return err
515 }
516 }
517 if *Verbose {
518 log.Printf("dedup len: %d\n", len(deduped))
519 }
520 reportXEDInconsistency(deduped)
521
522
523 slices.SortFunc(deduped, compareOperations)
524
525 typeMap := parseSIMDTypes(deduped)
526
527 archInfo := CurrentArch()
528 archLower := archInfo.Arch
529 archUpper := archInfo.ArchUpper
530
531 formatWriteAndClose(writeSIMDTypes(typeMap), path, "src/"+simdPackage+"/types_"+archLower+".go")
532
533 if archLower == "amd64" {
534 formatWriteAndClose(writeSIMDFeatures(deduped), path, "src/"+simdPackage+"/cpu.go")
535 }
536 f, fI := writeSIMDStubs(deduped, typeMap, archLower == "amd64")
537 formatWriteAndClose(f, path, "src/"+simdPackage+"/ops_"+archLower+".go")
538 formatWriteAndClose(fI, path, "src/"+simdPackage+"/ops_internal_"+archLower+".go")
539 formatWriteAndClose(writeSIMDIntrinsics(deduped, typeMap), path, "src/cmd/compile/internal/ssagen/simd"+archUpper+"intrinsics.go")
540 const simdGenericOpsFile = "src/cmd/compile/internal/ssa/_gen/simdgenericOps.go"
541 formatWriteAndClose(writeSIMDGenericOps(deduped, path+"/"+simdGenericOpsFile), path, simdGenericOpsFile)
542 formatWriteAndClose(writeSIMDMachineOps(deduped), path, "src/cmd/compile/internal/ssa/_gen/simd"+archUpper+"ops.go")
543 formatWriteAndClose(writeSIMDSSA(deduped), path, "src/cmd/compile/internal/"+archLower+"/simdssa.go")
544 writeAndClose(writeSIMDRules(deduped).Bytes(), path, "src/cmd/compile/internal/ssa/_gen/simd"+archUpper+".rules")
545
546 return nil
547 }
548
View as plain text