1
2
3
4
5 package main
6
7 import (
8 "bytes"
9 "flag"
10 "fmt"
11 "log"
12 "os"
13 "path/filepath"
14 "slices"
15 "strings"
16 "text/template"
17 "unicode"
18
19 "_gen/sgutil"
20 )
21
22 var (
23 gorootsrc = flag.String("gorootsrc", "../../../../../src", "root of destination directory, normally GOROOT/src")
24
25 genTypesFile = flag.String("types", "GOROOTSRC/simd/archsimd/types_wasm.go", "output file for simd types (e.g. types_wasm.go)")
26 genOpsFile = flag.String("ops", "GOROOTSRC/simd/archsimd/ops_wasm.go", "output file for simd ops (e.g. ops_wasm.go)")
27 genSSAOpsFile = flag.String("ssaops", "GOROOTSRC/cmd/compile/internal/ssa/_gen/simdWasmops.go", "output file for ssa ops (e.g. simdWasmops.go)")
28 genGenOpsFile = flag.String("genops", "GOROOTSRC/cmd/compile/internal/ssa/_gen/simdgenericOps.go", "output file for generic ssa ops (e.g. simdgenericOps.go)")
29 genSSARulesFile = flag.String("ssarules", "GOROOTSRC/cmd/compile/internal/ssa/_gen/simdWasm.rules", "output file for ssa rules (e.g. simdWasm.rules)")
30 genWasmSSAFile = flag.String("wasmssa", "GOROOTSRC/cmd/compile/internal/wasm/simdssa.go", "output file for wasm ssa (e.g. simdssa.go)")
31 genIntrinsicsFile = flag.String("intrinsics", "GOROOTSRC/cmd/compile/internal/ssagen/simdWasmintrinsics.go", "output file for intrinsics (e.g. simdWasmintrinsics.go)")
32
33 list = flag.Bool("list", false, "list all the opcodes")
34 )
35
36 type simdType struct {
37 Name string
38 Elem string
39 Count int
40 ElemSize int
41 Unsigned bool
42 Float bool
43 Methods map[string]*wasmOp
44 IntShaped *simdType
45 UintShaped *simdType
46 }
47
48 func (t *simdType) ElemBits() int {
49 return t.ElemSize
50 }
51
52 func (t *simdType) HalfCount() int {
53 return t.Count / 2
54 }
55
56 func (t *simdType) TwiceCount() int {
57 return t.Count * 2
58 }
59
60 func (t *simdType) Name_() string {
61 return t.Name
62 }
63
64 func (t *simdType) Article() string {
65
66 if t.Elem[0] == 'i' {
67 return "an"
68 }
69 return "a"
70 }
71
72 func (a *simdType) Compare(b *simdType) int {
73 if a.Name == b.Name {
74 return 0
75 }
76 if d := a.ElemSize - b.ElemSize; d != 0 {
77 return d
78 }
79 if d := a.Count - b.Count; d != 0 {
80
81 return d
82 }
83
84 ao := strings.Index("iIuUfFmM", a.Name[:1])
85 bo := strings.Index("iIuUfFmM", b.Name[:1])
86 if ao == -1 || bo == -1 {
87 panic(fmt.Errorf("a.Elem=%s, b.Elem=%s, unexpected first characters (should be in \"iIuUfFmM\")", a.Elem, b.Elem))
88 }
89 return ao - bo
90 }
91
92
93 func (s *simdType) WasmUName() string {
94 T := s.Name[:1]
95 if T == "U" {
96 T = "I"
97 }
98 return fmt.Sprintf("%s%dx%d", T, s.ElemSize, s.Count)
99 }
100
101
102 func (s *simdType) WasmLName() string {
103 T := s.Elem[:1]
104 if T == "u" {
105 T = "i"
106 }
107 return fmt.Sprintf("%s%dx%d", T, s.ElemSize, s.Count)
108 }
109
110 func CapitalizeFirst(s string) string {
111 return strings.ToUpper(s[:1]) + s[1:]
112 }
113
114 func (s *simdType) MaskFor() *simdType {
115 return maskFor[s]
116 }
117
118
119 func (s *simdType) WidenElements(newStem string) string {
120 return fmt.Sprintf("%s%dx%d", newStem, s.ElemSize*2, s.Count/2)
121 }
122
123
124 func (s *simdType) ShrinkElements(newStem string) string {
125 return fmt.Sprintf("%s%dx%d", newStem, s.ElemSize/2, s.Count*2)
126 }
127
128 func (s *simdType) IntFor() *simdType {
129 if s.IntShaped == nil {
130 return s
131 }
132 return s.IntShaped
133 }
134
135 func (s *simdType) UintFor() *simdType {
136 if s.UintShaped == nil {
137 return s
138 }
139 return s.UintShaped
140 }
141
142 func (s *simdType) String() string {
143 return s.Name
144 }
145
146 func (s *simdType) IsMask() bool {
147 return s.Name[0] == 'M'
148 }
149
150 func (u *simdType) setUintShaped(s *simdType) *simdType {
151 s.UintShaped = u
152 return u
153 }
154
155 const pkg = "simd/archsimd"
156
157 var (
158 vi8 = &simdType{"Int8x16", "int8", 16, 8, false, false, make(map[string]*wasmOp), nil, nil}
159 vi16 = &simdType{"Int16x8", "int16", 8, 16, false, false, make(map[string]*wasmOp), nil, nil}
160 vi32 = &simdType{"Int32x4", "int32", 4, 32, false, false, make(map[string]*wasmOp), nil, nil}
161 vi64 = &simdType{"Int64x2", "int64", 2, 64, false, false, make(map[string]*wasmOp), nil, nil}
162
163 vu8 = (&simdType{"Uint8x16", "uint8", 16, 8, true, false, make(map[string]*wasmOp), vi8, nil}).setUintShaped(vi8)
164 vu16 = (&simdType{"Uint16x8", "uint16", 8, 16, true, false, make(map[string]*wasmOp), vi16, nil}).setUintShaped(vi16)
165 vu32 = (&simdType{"Uint32x4", "uint32", 4, 32, true, false, make(map[string]*wasmOp), vi32, nil}).setUintShaped(vi32)
166 vu64 = (&simdType{"Uint64x2", "uint64", 2, 64, true, false, make(map[string]*wasmOp), vi64, nil}).setUintShaped(vi64)
167
168 vf32 = &simdType{"Float32x4", "float32", 4, 32, false, true, make(map[string]*wasmOp), vi32, vu32}
169 vf64 = &simdType{"Float64x2", "float64", 2, 64, false, true, make(map[string]*wasmOp), vi64, vu64}
170
171 vm8 = &simdType{"Mask8x16", "int8", 16, 8, false, false, make(map[string]*wasmOp), vi8, vu8}
172 vm16 = &simdType{"Mask16x8", "int16", 8, 16, false, false, make(map[string]*wasmOp), vi16, vu16}
173 vm32 = &simdType{"Mask32x4", "int32", 4, 32, false, false, make(map[string]*wasmOp), vi32, vu32}
174 vm64 = &simdType{"Mask64x2", "int64", 2, 64, false, false, make(map[string]*wasmOp), vi64, vu64}
175 )
176
177 var maskFor map[*simdType]*simdType = map[*simdType]*simdType{
178 vm8: vm8,
179 vi8: vm8,
180 vu8: vm8,
181 vm16: vm16,
182 vi16: vm16,
183 vu16: vm16,
184 vm32: vm32,
185 vi32: vm32,
186 vu32: vm32,
187 vf32: vm32,
188 vm64: vm64,
189 vi64: vm64,
190 vu64: vm64,
191 vf64: vm64,
192 }
193
194 type OpFlags uint16
195
196 const (
197 IsConst = OpFlags(1) << iota
198 IsLoad
199 IsStore
200 IsShift
201 IsSplat
202 IsBitwise
203 IsRelation
204 IsTest
205 IsExtract
206 IsCommutative
207 IsConversion
208 NameHasFormat
209 NonSigned
210 EmulatedRule
211 )
212
213 func (o OpFlags) OneString() string {
214 switch o {
215 case IsConst:
216 return "IsConst"
217 case IsLoad:
218 return "IsLoad"
219 case IsStore:
220 return "IsStore"
221 case IsShift:
222 return "IsShift"
223 case IsSplat:
224 return "IsSplat"
225 case IsBitwise:
226 return "IsBitwise"
227 case IsRelation:
228 return "IsRelation"
229 case IsTest:
230 return "IsTest"
231 case IsExtract:
232 return "IsExtract"
233 case IsCommutative:
234 return "IsCommutative"
235 case IsConversion:
236 return "IsConversion"
237 case NameHasFormat:
238 return "NameHasFormat"
239 case NonSigned:
240 return "NonSigned"
241 case EmulatedRule:
242 return "RmulatedRule"
243 }
244 return fmt.Sprintf("0x%x", o)
245 }
246
247 var allFlags = []OpFlags{
248 IsConst,
249 IsLoad,
250 IsStore,
251 IsShift,
252 IsSplat,
253 IsBitwise,
254 IsRelation,
255 IsTest,
256 IsExtract,
257 IsCommutative,
258 IsConversion,
259 NameHasFormat,
260 NonSigned,
261 EmulatedRule,
262 }
263
264 func (o OpFlags) String() string {
265 sep := ""
266 ret := ""
267
268 for _, x := range allFlags {
269 if x&o != 0 {
270 ret += sep + x.OneString()
271 sep = "+"
272 }
273 }
274 return ret
275 }
276
277
278 type wasmOp struct {
279 t *simdType
280 op string
281 argCount int
282 argType string
283 resultType string
284 opFlags OpFlags
285 doc string
286
287 immRange uint8
288 immName string
289 arg1Name string
290 arg2Name string
291 }
292
293 func (o *wasmOp) String() string {
294 return fmt.Sprintf(
295 "t=%s, op=%s, arity=%d, Method=%s, ArgType=%s, ResultType=%s, GenOp=%s, SsaWasmOp=%s, AsmOp=%s, WasmInstruction=%s, ImmRange=%d, ImmName=%s, Flags=%s",
296 o.Type().Name, o.Op(), o.ArgCount(), o.Method(), o.ArgType(), o.ResultType(), o.SsaGenOp(),
297 o.SsaWasmOp(), o.AsmOp(), o.WasmInstruction(), o.ImmRange(), o.ImmName(), o.OpFlags())
298 }
299
300 func compareWasmOps(a, b *wasmOp) int {
301 am, bm := a.NUMethod(), b.NUMethod()
302 if cmp := strings.Compare(am, bm); cmp != 0 {
303 return cmp
304 }
305 if cmp := a.t.Compare(b.t); cmp != 0 {
306 return cmp
307 }
308 return strings.Compare(a.op, b.op)
309 }
310
311 func (o *wasmOp) Type() *simdType {
312 return o.t
313 }
314
315 func (o *wasmOp) ArgCount() int {
316 return o.argCount
317 }
318
319 func (o *wasmOp) OpFlags() OpFlags {
320 return o.opFlags
321 }
322
323 func (o *wasmOp) Flag(f OpFlags) bool {
324 return o.opFlags&f != 0
325 }
326
327 func (o *wasmOp) ImmRange() uint8 {
328 return o.immRange
329 }
330
331 func (o *wasmOp) ImmName() string {
332 if o.immName == "" {
333 return "_"
334 }
335 return o.immName
336 }
337
338 func snakeToCamel(s string) string {
339 capnext := true
340 result := ""
341 for _, c := range s {
342 if c == '_' {
343 capnext = true
344 continue
345 }
346 if '0' <= c && c <= '9' {
347 capnext = true
348 } else {
349 if capnext {
350 c = unicode.ToUpper(c)
351 capnext = false
352 }
353 }
354 result += string(c)
355 }
356 return result
357 }
358
359
360
361 func (o *wasmOp) Op() string {
362 return snakeToCamel(o.op)
363 }
364
365 func (o *wasmOp) T() *simdType {
366 return o.t
367 }
368
369
370
371 func (o *wasmOp) Method() string {
372 goname := gonames[o.op]
373 if o.Flag(NameHasFormat) {
374
375 count := o.T().Count / 2
376 x := strings.Index(o.ResultType(), "x")
377 typ := o.ResultType()[:x]
378 return fmt.Sprintf(goname, count, typ)
379 }
380 if len(goname) >= 1 {
381 if len(goname) == 1 {
382
383 return ""
384 }
385 return goname
386 }
387 return snakeToCamel(o.op)
388 }
389
390 func (o *wasmOp) NUMethod() string {
391 s := o.Method()
392 if s != "" && s[0] == '_' {
393 s = s[1:]
394 }
395 return s
396 }
397
398 func (o *wasmOp) SsaResultType() string {
399 if o.Flag(IsTest) {
400 return "Bool"
401 }
402 return "Vec128"
403 }
404
405 func (o *wasmOp) RegInfo() string {
406 if o.argType == "" {
407 if o.resultType == "" {
408 switch o.argCount {
409 case 1:
410 return "v11"
411 case 2:
412 return "v21"
413 case 3:
414 return "v31"
415 }
416 } else if o.Flag(IsConversion) {
417 if o.argCount == 1 {
418 return "v11"
419 } else if o.argCount == 2 {
420
421 return "v21"
422 }
423 } else {
424 if o.argCount == 1 {
425
426 if o.resultType[0] == 'i' || o.resultType[0] == 'u' {
427 return "v11gp"
428 }
429 if o.resultType == "float32" {
430 return "v11fp32"
431 }
432 if o.resultType == "float64" {
433 return "v11fp64"
434 }
435 }
436 }
437 } else if o.argCount == 2 {
438
439 if o.argType[0] == 'i' || o.argType[0] == 'u' {
440 return "v1gpv"
441 } else if o.argType == "float32" {
442 return "v1fp32v"
443 } else if o.argType == "float64" {
444 return "v1fp64v"
445 }
446 } else if o.argCount == 3 {
447
448 return "v31"
449 } else if o.Flag(IsSplat) {
450 if o.argType[0] == 'i' {
451 return "gpv"
452 } else if o.argType == "float32" {
453 return "fp32v"
454 } else {
455 return "fp64v"
456 }
457 }
458
459 panic("RegInfo not implemented for " + o.String())
460 }
461
462 func (o *wasmOp) DefinesGeneric() bool {
463 return o.Method() != "" && !o.T().IsMask() && !o.Flag(NonSigned)
464 }
465
466
467 func (o *wasmOp) SsaGenOp() string {
468 m := o.Method()
469 if m == "" {
470 return ""
471 }
472
473
474
475 if m[0] == '_' {
476
477 m = m[1:]
478 }
479 t := o.T()
480 if t.IsMask() || o.Flag(NonSigned) {
481 t = t.IntShaped
482 }
483
484 if strings.HasPrefix(o.op, "RotateAll") {
485 m += "Var"
486 }
487 r := m + t.Name
488 return r
489 }
490
491
492
493 func (o *wasmOp) conversionCorrectedOp() (op, lowerSuffix, upperSuffix string) {
494 op = o.op
495 if strings.HasSuffix(op, "_s") {
496 op = op[:len(op)-2]
497 upperSuffix = "S"
498 lowerSuffix = "_s"
499 } else if strings.HasSuffix(op, "_u") {
500 op = op[:len(op)-2]
501 upperSuffix = "U"
502 lowerSuffix = "_u"
503 }
504 return
505 }
506
507
508
509 func (o *wasmOp) SsaWasmOp() string {
510
511
512 oop := o.Op()
513 if oop == "BitSelect" {
514
515
516
517 oop = "Bitselect"
518 } else if oop == "Shuffle" {
519 oop += "16"
520 }
521
522 if o.Flag(IsBitwise) {
523 return "V128" + oop
524 }
525 if o.Flag(IsConversion) {
526
527 op, _, suffix := o.conversionCorrectedOp()
528 op = snakeToCamel(op)
529 return CapitalizeFirst(wasmResultType(o.ResultType())) + op + o.T().WasmUName() + suffix
530 }
531 r := o.T().WasmUName() + oop
532 return r
533 }
534
535 func wasmResultType(s string) string {
536 ti := strings.Index(s, "t")
537 if s[ti-1] == 'n' {
538 s = "i" + s[ti+1:]
539 } else {
540 s = "f" + s[ti+1:]
541 }
542 return s
543 }
544
545
546
547 func (o *wasmOp) WasmInstruction() string {
548 oop := o.op
549 if o.Flag(IsBitwise) {
550 return "v128" + "." + oop
551 }
552 if o.Flag(IsConversion) {
553
554 op, suffix, _ := o.conversionCorrectedOp()
555 return wasmResultType(o.ResultType()) + "." + op + "_" + o.T().WasmLName() + suffix
556 }
557 r := o.T().WasmLName() + "." + oop
558 return r
559 }
560
561 func (o *wasmOp) RcvrType() string {
562 return o.T().Name
563 }
564
565 func (o *wasmOp) Arg2Name() string {
566 if n := o.arg2Name; n != "" {
567 return n
568 }
569 return "z"
570 }
571
572 func (o *wasmOp) Arg1Name() string {
573 if n := o.arg1Name; n != "" {
574 return n
575 }
576 return "y"
577 }
578
579
580 func (o *wasmOp) ResultType() string {
581 if o.Flag(IsRelation) {
582 return o.T().MaskFor().Name
583 }
584 if o.resultType == "" {
585 return o.T().Name
586 }
587 return o.resultType
588 }
589
590 func (o *wasmOp) ArgType() string {
591 if o.argType == "" {
592 return o.T().Name
593 }
594 return o.argType
595 }
596
597
598 func (o *wasmOp) AsmOp() string {
599 r := o.SsaWasmOp()
600 return r
601 }
602
603 var (
604
605
606 allTypes = []*simdType{vi8, vi16, vi32, vi64, vu8, vu16, vu32, vu64, vf32, vf64}
607 signed = []*simdType{vi8, vi16, vi32, vi64}
608 unsigned = []*simdType{vu8, vu16, vu32, vu64}
609 ints = []*simdType{vi8, vi16, vi32, vi64, vu8, vu16, vu32, vu64}
610 floats = []*simdType{vf32, vf64}
611 sle16 = []*simdType{vi8, vi16}
612 ule16 = []*simdType{vu8, vu16}
613 ige16 = []*simdType{vi16, vi32, vi64, vu16, vu32, vu64}
614 nge32 = []*simdType{vi32, vi64, vu32, vu64, vf32, vf64}
615 sle32 = []*simdType{vi8, vi16, vi32}
616 ule32 = []*simdType{vu8, vu16, vu32}
617 ieq32 = []*simdType{vi32, vu32}
618 masks = []*simdType{vm8, vm16, vm32, vm64}
619
620
621
622
623
624
625
626
627
628
629
630
631 iv_1 = []string{"not"}
632 iv_2 = []string{"and", "andnot", "or", "xor"}
633 v_3 = []string{"bitSelect"}
634
635 v_t = []string{"any_true"}
636
637 s_1 = []string{"abs", "neg"}
638 f_1 = []string{"abs", "neg", "sqrt", "ceil", "floor", "trunc", "nearest"}
639 i8_1 = []string{"popcnt"}
640
641 i_2 = []string{"add", "sub"}
642 sle16_2 = []string{"add_sat_s", "sub_sat_s"}
643 ule16_2 = []string{"add_sat_u", "sub_sat_u", "avgr_u"}
644 ige16_2 = []string{"mul"}
645 s16_2 = []string{"q15mulr_sat_s", "relaxed_q15mulr_s"}
646 sle32_2 = []string{"min_s", "max_s"}
647 ule32_2 = []string{"min_u", "max_u"}
648
649 f_2 = []string{"add", "sub", "mul", "div", "min", "max", "pmin", "pmax", "relaxed_min", "relaxed_max"}
650
651 i_3 = []string{"relaxed_laneselect"}
652
653 f_3 = []string{"relaxed_madd", "relaxed_nmadd"}
654
655 i_t = []string{"all_true"}
656
657
658
659
660 i_r = []string{"eq", "ne"}
661 s_r = []string{"lt_s", "gt_s", "le_s", "ge_s"}
662 ule32_r = []string{"lt_u", "gt_u", "le_u", "ge_u"}
663
664 f_r = []string{"eq", "ne", "lt", "gt", "le", "ge"}
665
666
667 i8_swiz = []string{"swizzle"}
668 i8_shuf = []string{"shuffle"}
669
670
671 u_s = []string{"shl", "shr_u"}
672 s_s = []string{"shl", "shr_s"}
673
674 sle16_q1 = []string{"extadd_pairwise_s"}
675 ule16_q1 = []string{"extadd_pairwise_u"}
676
677 s_q2 = []string{"extmul_low_s", "extmul_high_s"}
678 u_q2 = []string{"extmul_low_u", "extmul_high_u"}
679 s16_q2 = []string{"dot_s"}
680 s8_q2 = []string{"relaxed_dot_s"}
681
682 s8_q3 = []string{"relaxed_dot_add_s"}
683
684
685
686
687 extend_u = []string{"extend_low_u", "extend_high_u"}
688 extend_s = []string{"extend_low_s", "extend_high_s"}
689 convert_low_u = []string{"convert_low_u"}
690 convert_low_s = []string{"convert_low_s"}
691
692
693 convert_s = []string{"convert_s"}
694 convert_u = []string{"convert_u"}
695
696 f_c = []string{"trunc_sat_s", "trunc_sat_u"}
697 f32_c = []string{"promote_low"}
698 f64_c = []string{"demote_zero"}
699
700
701 nge32_x = []string{"extract_lane"}
702 sle16_x = []string{"extract_lane_s"}
703 ule16_x = []string{"extract_lane_u"}
704
705
706 n_x = []string{"replace_lane"}
707
708 rotates = []string{"RotateAllLeft", "RotateAllRight"}
709
710
711
712
713
714
715
716
717
718 gonames = map[string]string{
719
720 "any_true": "-",
721
722 "add_sat_s": "AddSaturated",
723 "add_sat_u": "AddSaturated",
724 "sub_sat_s": "SubSaturated",
725 "sub_sat_u": "SubSaturated",
726 "andnot": "AndNot",
727 "nearest": "Round",
728 "popcnt": "OnesCount",
729 "avgr_u": "Average",
730 "eq": "Equal",
731 "ne": "NotEqual",
732 "le": "LessEqual",
733 "ge": "GreaterEqual",
734 "lt": "Less",
735 "gt": "Greater",
736 "le_s": "LessEqual",
737 "ge_s": "GreaterEqual",
738 "lt_s": "Less",
739 "gt_s": "Greater",
740 "le_u": "LessEqual",
741 "ge_u": "GreaterEqual",
742 "lt_u": "Less",
743 "gt_u": "Greater",
744 "relaxed_madd": "MulAdd",
745 "shl": "ShiftAllLeft",
746
747 "extract_lane": "GetElem",
748 "extract_lane_s": "GetElem",
749 "extract_lane_u": "GetElem",
750
751 "replace_lane": "SetElem",
752
753 "bitselect": "BitSelect",
754
755
756 "min_s": "Min",
757 "max_s": "Max",
758 "min_u": "Min",
759 "max_u": "Max",
760 "shr_u": "ShiftAllRight",
761 "shr_s": "ShiftAllRight",
762
763
764 "q15mulr_sat_s": "?",
765 "relaxed_q15mulr_s": "?",
766
767 "pmin": "-",
768 "pmax": "-",
769 "relaxed_min": "-",
770 "relaxed_max": "-",
771
772
773 "relaxed_nmadd": "-",
774
775 "all_true": "?",
776
777 "extadd_pairwise_s": "?",
778 "extadd_pairwise_u": "?",
779 "extmul_low_s": "MulWidenLo",
780 "extmul_low_u": "MulWidenLo",
781 "extmul_high_s": "MulWidenHi",
782 "extmul_high_u": "MulWidenHi",
783 "dot_s": "?",
784 "relaxed_dot_s": "?",
785
786
787 "extend_low_s": "ExtendLo%dTo%s",
788 "extend_high_s": "ExtendHi%dTo%s",
789 "extend_low_u": "ExtendLo%dTo%s",
790 "extend_high_u": "ExtendHi%dTo%s",
791
792
793 "convert_s": "ConvertToFloat32",
794 "convert_u": "ConvertToFloat32",
795
796
797 "trunc_sat_s": "ConvertToInt32",
798 "trunc_sat_u": "ConvertToUint32",
799
800
801 "convert_low_s": "ConvertLo2ToFloat64",
802 "convert_low_u": "ConvertLo2ToFloat64",
803
804 "swizzle": "LookupOrZero",
805 "splat": "Broadcast",
806 }
807 )
808
809 func initWasmOps() {
810
811 isBitwise := func(s string, _ *simdType) OpFlags {
812 return IsBitwise
813 }
814 isTest := func(s string, _ *simdType) OpFlags {
815 if s == "any_true" {
816 return IsBitwise | IsTest
817 }
818 return IsTest
819 }
820 binBitwise := func(s string, t *simdType) OpFlags {
821 if s == "andnot" {
822 return IsBitwise
823 }
824 return IsBitwise | IsCommutative
825 }
826 unShape := func(s string, t *simdType) OpFlags {
827 return 0
828 }
829 binShape := func(s string, t *simdType) OpFlags {
830 if !t.Float && t.IntShaped != nil {
831 if s == "add" {
832 return IsCommutative | NonSigned
833 }
834 if s == "sub" {
835 return NonSigned
836 }
837 }
838 if strings.HasPrefix(s, "sub") || s == "div" {
839 return 0
840 }
841 return IsCommutative
842 }
843 isMask := func(s string, t *simdType) OpFlags {
844 flags := IsRelation
845 if s == "eq" || s == "ne" {
846 flags |= NonSigned
847 } else {
848 flags |= IsBitwise
849 }
850 if s == "ne" || s == "eq" || s == "and" || s == "or" || s == "xor" {
851 flags |= IsCommutative
852 }
853 return flags
854 }
855
856 isRelation := func(s string, t *simdType) OpFlags {
857 flags := IsRelation
858
859 if s == "ne" || s == "eq" || s == "and" || s == "or" || s == "xor" {
860 flags |= IsCommutative
861 }
862 return flags
863 }
864
865 bitSelect := func(op *wasmOp) {
866 op.opFlags = IsBitwise
867 op.arg2Name = "cond"
868 }
869
870 addWasmOps(ints, iv_1, 1, isBitwise)
871 addWasmOps(ints, iv_2, 2, binBitwise)
872 addWasmOpsDetail(ints, v_3, 3, bitSelect)
873 addWasmOps(allTypes, v_t, 1, isTest)
874 addWasmOps(signed, s_1, 1, unShape)
875 addWasmOps(floats, f_1, 1, unShape)
876 addWasmOps([]*simdType{vi8}, i8_1, 1, nil)
877
878 addWasmOps(ints, i_2, 2, binShape)
879 addWasmOps(sle16, sle16_2, 2, binShape)
880 addWasmOps(ule16, ule16_2, 2, binShape)
881 addWasmOps(ige16, ige16_2, 2, binShape)
882
883 addWasmOps([]*simdType{vi16}, s16_2, 2, nil)
884
885 addWasmOps(sle32, sle32_2, 2, binShape)
886 addWasmOps(ule32, ule32_2, 2, binShape)
887
888 addWasmOps(floats, f_2, 2, binShape)
889
890 addWasmOps(floats, f_3, 3, nil)
891
892 addWasmOps(ints, i_t, 1, isTest)
893
894 addWasmOps(ints, i_r, 2, isRelation)
895 addWasmOps(signed, s_r, 2, isRelation)
896 addWasmOps(ule32, ule32_r, 2, isRelation)
897
898 addWasmOps(floats, f_r, 2, isRelation)
899
900
901
902 addWasmOpsDetail([]*simdType{vi8}, i8_swiz, 2, func(op *wasmOp) { op.arg1Name = "i" })
903
904
905 addWasmOps(masks, iv_2, 2, isMask)
906
907 extractImmediate := func(op *wasmOp) {
908 op.resultType = op.t.Elem
909 op.immRange = uint8(op.t.Count)
910 op.immName = "index"
911 }
912
913 addWasmOpsDetail(nge32, nge32_x, 1, extractImmediate)
914 addWasmOpsDetail(sle16, sle16_x, 1, extractImmediate)
915 addWasmOpsDetail(ule16, ule16_x, 1, extractImmediate)
916
917 replaceImmediate := func(op *wasmOp) {
918 op.argType = op.t.Elem
919 op.immRange = uint8(op.t.Count)
920 op.immName = "index"
921 }
922 addWasmOpsDetail(allTypes, n_x, 2, replaceImmediate)
923
924 shift := func(op *wasmOp) {
925 op.argType = "uint64"
926 op.opFlags = IsShift
927 }
928 addWasmOpsDetail(signed, s_s, 2, shift)
929 addWasmOpsDetail(unsigned, u_s, 2, shift)
930
931 splat := func(op *wasmOp) {
932 op.opFlags = IsSplat
933 op.argType = op.t.Elem
934 op.resultType = op.t.Name
935 if op.argType[0] == 'u' {
936 op.opFlags |= NonSigned
937 }
938 }
939 addWasmOpsDetail(allTypes, []string{"splat"}, 1, splat)
940
941
942 extendHalf := func(op *wasmOp) {
943 t := op.t
944 op.opFlags = IsConversion | NameHasFormat
945
946 stem := "Int"
947 if op.op[len(op.op)-1] == 'u' {
948 stem = "Uint"
949 }
950 op.resultType = t.WidenElements(stem)
951 }
952 mulHalf := func(op *wasmOp) {
953 t := op.t
954 op.opFlags = IsConversion | IsCommutative
955
956 stem := "Int"
957 if op.op[len(op.op)-1] == 'u' {
958 stem = "Uint"
959 }
960 op.resultType = t.WidenElements(stem)
961 }
962 convertHalf := func(op *wasmOp) {
963
964
965 op.opFlags = IsConversion
966 op.resultType = "Float64x2"
967 }
968 convert := func(op *wasmOp) {
969 op.opFlags = IsConversion
970 op.resultType = "Float32x4"
971 }
972 truncSat := func(op *wasmOp) {
973 op.opFlags = IsConversion
974 if op.op == "trunc_sat_s" {
975 op.resultType = "Int32x4"
976 } else {
977 op.resultType = "Uint32x4"
978 }
979 }
980
981 rotate := func(op *wasmOp) {
982 op.opFlags = EmulatedRule | IsShift
983 op.argType = "uint64"
984 op.arg1Name = "shift"
985 }
986
987 addWasmOpsDetail(ule32, extend_u, 1, extendHalf)
988 addWasmOpsDetail(sle32, extend_s, 1, extendHalf)
989 addWasmOpsDetail([]*simdType{vi32}, convert_low_s, 1, convertHalf)
990 addWasmOpsDetail([]*simdType{vu32}, convert_low_u, 1, convertHalf)
991 addWasmOpsDetail([]*simdType{vi32}, convert_s, 1, convert)
992 addWasmOpsDetail([]*simdType{vu32}, convert_u, 1, convert)
993
994 addWasmOpsDetail([]*simdType{vf32}, f_c, 1, truncSat)
995
996 addWasmOpsDetail(sle32, s_q2, 2, mulHalf)
997 addWasmOpsDetail(ule32, u_q2, 2, mulHalf)
998
999 addWasmOpsDetail(ints, rotates, 2, rotate)
1000
1001 slices.SortFunc(wasmOps, compareWasmOps)
1002
1003 for i := 1; i < len(wasmOps); i++ {
1004 c := compareWasmOps(wasmOps[i-1], wasmOps[i])
1005 if c >= 0 {
1006 d := compareWasmOps(wasmOps[i-1], wasmOps[i])
1007 fmt.Printf("Two wasm ops compared out of order, c=%d, \n%v\n%v\n", d, wasmOps[i-1], wasmOps[i])
1008 }
1009 }
1010 }
1011
1012 var wasmOps = []*wasmOp{}
1013
1014
1015
1016
1017 func addWasmOpsDetail(types []*simdType, ops []string, argCount int, after func(op *wasmOp)) {
1018 for _, t := range types {
1019 for _, o := range ops {
1020 op := &wasmOp{t: t, op: o, argCount: argCount}
1021 after(op)
1022 if t.Methods[o] != nil {
1023 panic("Double addition of method " + o + " for " + t.Name)
1024 }
1025 t.Methods[o] = op
1026 wasmOps = append(wasmOps, op)
1027 }
1028 }
1029
1030 }
1031
1032
1033
1034
1035 func addWasmOps(types []*simdType, ops []string, argCount int, flags func(op string, ty *simdType) OpFlags) {
1036 addWasmOpsDetail(types, ops, argCount, func(op *wasmOp) {
1037 if flags == nil {
1038 return
1039 }
1040 op.opFlags = flags(op.op, op.T())
1041 })
1042 }
1043
1044 func must(err error) {
1045 if err != nil {
1046 panic(err)
1047 }
1048 }
1049
1050 func mustVal[T any](v T, err error) T {
1051 if err != nil {
1052 panic(err)
1053 }
1054 return v
1055 }
1056
1057 func main() {
1058
1059 flag.Parse()
1060
1061 initWasmOps()
1062
1063 if *list {
1064
1065 for i := range wasmOps {
1066 fmt.Println(wasmOps[i])
1067 }
1068 return
1069 }
1070
1071 *genTypesFile = mustVal(filepath.Abs(strings.ReplaceAll(*genTypesFile, "GOROOTSRC", *gorootsrc)))
1072 *genOpsFile = mustVal(filepath.Abs(strings.ReplaceAll(*genOpsFile, "GOROOTSRC", *gorootsrc)))
1073 *genSSAOpsFile = mustVal(filepath.Abs(strings.ReplaceAll(*genSSAOpsFile, "GOROOTSRC", *gorootsrc)))
1074 *genGenOpsFile = mustVal(filepath.Abs(strings.ReplaceAll(*genGenOpsFile, "GOROOTSRC", *gorootsrc)))
1075 *genSSARulesFile = mustVal(filepath.Abs(strings.ReplaceAll(*genSSARulesFile, "GOROOTSRC", *gorootsrc)))
1076 *genWasmSSAFile = mustVal(filepath.Abs(strings.ReplaceAll(*genWasmSSAFile, "GOROOTSRC", *gorootsrc)))
1077 *genIntrinsicsFile = mustVal(filepath.Abs(strings.ReplaceAll(*genIntrinsicsFile, "GOROOTSRC", *gorootsrc)))
1078
1079 log.Println("types file =", *genTypesFile)
1080 log.Println("ops file =", *genOpsFile)
1081 log.Println("ssa ops file =", *genSSAOpsFile)
1082 log.Println("ssa generic ops file =", *genGenOpsFile)
1083 log.Println("ssa rules file =", *genSSARulesFile)
1084 log.Println("ssa wasm ops file =", *genWasmSSAFile)
1085 log.Println("intrinsics file =", *genIntrinsicsFile)
1086
1087 log.Println("Generating WASM SIMD files...")
1088
1089 sgutil.FormatWriteAndClose(genTypes(), *genTypesFile)
1090 sgutil.FormatWriteAndClose(genOps(), *genOpsFile)
1091 sgutil.FormatWriteAndClose(genSSAOps(), *genSSAOpsFile)
1092 sgutil.FormatWriteAndClose(genWasmSSA(), *genWasmSSAFile)
1093 sgutil.FormatWriteAndClose(genIntrinsics(), *genIntrinsicsFile)
1094 sgutil.FormatWriteAndClose(genGenerics(), *genGenOpsFile)
1095
1096 genSSARules()
1097
1098 }
1099
1100 func templateOf(name, text string) *template.Template {
1101 return template.Must(template.New(name).Parse(text))
1102 }
1103
1104 var loadDecl = templateOf("load from array", `
1105 // Load{{.Name}}Array loads {{.Article}} {{.Name}} from a [{{.Count}}]{{.Elem}}.
1106 //
1107 //go:noescape
1108 func Load{{.Name}}Array(y *[{{.Count}}]{{.Elem}}) {{.Name}}
1109
1110 // Load{{.Name}} loads {{.Article}} {{.Name}} from a slice of at least {{.Count}} {{.Elem}}s.
1111 func Load{{.Name}}(s []{{.Elem}}) {{.Name}} {
1112 return Load{{.Name}}Array((*[{{.Count}}]{{.Elem}})(s))
1113 }
1114 `)
1115
1116 var storeDecl = templateOf("store to array", `
1117 // StoreArray stores {{.Article}} {{.Name}} to a [{{.Count}}]{{.Elem}}.
1118 //
1119 //go:noescape
1120 func (x {{.Name}}) StoreArray(y *[{{.Count}}]{{.Elem}})
1121
1122 // Store stores x into a slice of at least {{.Count}} {{.Elem}}s.
1123 func (x {{.Name}}) Store(s []{{.Elem}}) {
1124 x.StoreArray((*[{{.Count}}]{{.Elem}})(s))
1125 }
1126 `)
1127
1128 var splatDecl = templateOf("broadcast an element", `
1129 // Broadcast{{.Name}} broadcasts {{.Article}} {{.Elem}} to all elements of {{.Article}} {{.Name}} vector.
1130 func Broadcast{{.Name}}(x {{.Elem}}) {{.Name}}
1131 `)
1132
1133 var typeDecl = templateOf("type decl", `
1134 // {{.Name}} is a 128-bit SIMD vector of {{.Count}} {{.Elem}}s.
1135 type {{.Name}} struct {
1136 {{.Elem}}x{{.Count}} v128
1137 vals [{{.Count}}]{{.Elem}}
1138 }
1139 `)
1140
1141 var maskTypeDecl = templateOf("type decl", `
1142 // {{.Name}} is a 128-bit SIMD mask of {{.Count}} {{.Elem}}s.
1143 type {{.Name}} struct {
1144 {{.Elem}}x{{.Count}} v128
1145 vals [{{.Count}}]{{.Elem}}
1146 }
1147 `)
1148
1149 var lenDecl = templateOf("len decl", `
1150 // Len returns the number of elements in {{.Article}} {{.Name}}.
1151 func (x {{.Name}}) Len() int { return {{.Count}} }
1152 `)
1153
1154 func genTypes() (f *bytes.Buffer) {
1155 f = new(bytes.Buffer)
1156 fmt.Fprintln(f, "// Code generated by 'wasmgen'; DO NOT EDIT.")
1157 fmt.Fprintln(f)
1158 fmt.Fprintln(f, "//go:build goexperiment.simd && wasm")
1159 fmt.Fprintln(f)
1160 fmt.Fprintln(f, "package archsimd")
1161 fmt.Fprintln(f)
1162 fmt.Fprintln(f, "// v128 is a tag type that tells the compiler that this is really 128-bit SIMD")
1163 fmt.Fprintln(f, "type v128 struct {")
1164 fmt.Fprintln(f, "\t_128 [0]func() // uncomparable")
1165 fmt.Fprintln(f, "}")
1166
1167 for _, t := range allTypes {
1168 typeDecl.Execute(f, t)
1169 lenDecl.Execute(f, t)
1170 loadDecl.Execute(f, t)
1171 storeDecl.Execute(f, t)
1172 splatDecl.Execute(f, t)
1173 }
1174 for _, t := range masks {
1175 maskTypeDecl.Execute(f, t)
1176 }
1177 return
1178 }
1179
1180 var docForOp map[string]string = map[string]string{
1181 "Add": " returns the result of adding x and y, elementwise.",
1182 "Sub": " returns the result of subtracting y from x, elementwise.",
1183 "Mul": " returns the result of multiplying x and y, elementwise.",
1184 "Div": " returns the result of dividing x by y, elementwise.",
1185 "Neg": " returns the elementwise negation of x.",
1186 "Abs": " returns the elementwise absolute value of x.",
1187 "Sqrt": " returns the elementwise square root of x.",
1188 "Not": " returns the bitwise NOT of x.",
1189 "And": " returns the bitwise AND of x and y.",
1190 "Or": " returns the bitwise OR of x and y.",
1191 "Xor": " returns the bitwise XOR of x and y.",
1192 "AndNot": " returns the bitwise AND NOT of x and y (x & ^y).",
1193 "Min": " returns the elementwise minimum of x and y.",
1194 "Max": " returns the elementwise maximum of x and y.",
1195 "Round": " returns the elementwise nearest integer, rounding ties to even.",
1196 "OnesCount": " returns the elementwise population count (number of bits set).",
1197 "Average": " returns the elementwise average of unsigned integers in x and y.",
1198 "Equal": " returns true if x equals y, elementwise.",
1199 "NotEqual": " returns true if x does not equal y, elementwise.",
1200 "Less": " returns true if x is less than y, elementwise.",
1201 "Greater": " returns true if x is greater than y, elementwise.",
1202 "LessEqual": " returns true if x is less than or equal to y, elementwise.",
1203 "GreaterEqual": " returns true if x is greater than or equal to y, elementwise.",
1204 "MulAdd": " returns the elementwise multiply-add of x, y, and z.",
1205 "ShiftAllLeft": " returns the elementwise left shift of x by y bits.",
1206 "ShiftAllRight": " returns the elementwise right shift of x by y bits.",
1207 "Ceil": " returns the elementwise ceiling of x.",
1208 "Floor": " returns the elementwise floor of x.",
1209 "Trunc": " returns the elementwise truncation of x.",
1210 "BitSelect": " returns the bitwise selection if mask[i] then x[i] else y[i]",
1211 "GetElem": " gets the lane value at the given index.",
1212 "SetElem": " sets the lane at the given index to y.",
1213 "TruncSatS": " returns the elementwise saturating signed conversion to integer.",
1214 "TruncSatU": " returns the elementwise saturating unsigned conversion to integer.",
1215 "PromoteLow": " promotes the lower half elements of x to double width values.",
1216 "DemoteZero": " demotes elements of x to half width values in lower elements of result, with zeroes in the upper elements",
1217 "ConvertToFloat32": " converts elements of x to Float32x4.",
1218 "ConvertToInt32": " converts elements of x to Int32x4.",
1219 "ConvertToUint32": " converts elements of x to Uint32x4.",
1220 "ConvertLo2ToFloat64": " converts the first two elements of x to Float64x2.",
1221 "MulWidenHi": ` returns the doubled-width product of respective elements of the upper halves of x and y.
1222 //
1223 // Result[i] = x[i+{{.Type.HalfCount}}] * y[i+{{.Type.HalfCount}}], for 0 <= i < {{.Type.HalfCount}} == |x|/2.`,
1224 "MulWidenLo": ` returns the doubled-width product of respective elements of the lower halves of x and y.
1225 //
1226 // Result[i] = x[i] * y[i], for 0 <= i < {{.Type.HalfCount}} == |x|/2.`,
1227
1228 "ExtendLo8ToInt16": " extends the lower 8 elements of x to 16-bit integers.",
1229 "ExtendLo16ToInt32": " extends the lower 4 elements of x to 32-bit integers.",
1230 "ExtendLo32ToInt64": " extends the lower 2 elements of x to 64-bit integers.",
1231 "ExtendHi8ToInt16": " extends the higher 8 elements of x to 16-bit integers.",
1232 "ExtendHi16ToInt32": " extends the higher 4 elements of x to 32-bit integers.",
1233 "ExtendHi32ToInt64": " extends the higher 2 elements of x to 64-bit integers.",
1234 "ExtendLo8ToUint16": " extends the lower 8 elements of x to 16-bit unsigned integers.",
1235 "ExtendLo16ToUint32": " extends the lower 4 elements of x to 32-bit unsigned integers.",
1236 "ExtendLo32ToUint64": " extends the lower 2 elements of x to 64-bit unsigned integers.",
1237 "ExtendHi8ToUint16": " extends the higher 8 elements of x to 16-bit unsigned integers.",
1238 "ExtendHi16ToUint32": " extends the higher 4 elements of x to 32-bit unsigned integers.",
1239 "ExtendHi32ToUint64": " extends the higher 2 elements of x to 64-bit unsigned integers.",
1240 "AddSaturated": " returns the result of adding x and y, saturating instead of overflowing, elementwise.",
1241 "SubSaturated": " returns the result of subtracting x and y, saturating instead of overflowing, elementwise.",
1242 "Shuffle": " returns the elements of y concatenated with z that are selected by elements of x",
1243 "LookupOrZero": ` returns the elements of x as indexed by the elements of i. If an index is out of range, its result is 0.
1244 //
1245 // if 0 <= indices[i] && indices[i] < len(table) {
1246 // result[i] = table[indices[i]]
1247 // } else {
1248 // result[i] = 0
1249 // }`,
1250 "RelaxedSwizzle": "",
1251 "RelaxedLaneselect": "",
1252 }
1253
1254 func (w *wasmOp) DocRest() string {
1255 m := w.Method()
1256 d := docForOp[m]
1257 if d == "" {
1258 return ""
1259 }
1260
1261 var buf bytes.Buffer
1262 if e := templateOf(m, d).Execute(&buf, w); e != nil {
1263 panic(e)
1264 }
1265 return buf.String()
1266 }
1267
1268 var unOp = templateOf("unaryOp", `
1269 // {{.Method}}{{.DocRest}}
1270 //
1271 // Asm: {{.AsmOp}}
1272 func (x {{.RcvrType}}) {{.Method}}() {{.ResultType}}
1273 `)
1274
1275 var binOp = templateOf("binaryOp", `
1276 // {{.Method}}{{.DocRest}}
1277 //
1278 // Asm: {{.AsmOp}}
1279 func (x {{.RcvrType}}) {{.Method}}({{.Arg1Name}} {{.ArgType}}) {{.ResultType}}
1280 `)
1281
1282 var ternOp = templateOf("ternaryOp", `
1283 // {{.Method}}{{.DocRest}}
1284 //
1285 // Asm: {{.AsmOp}}
1286 func (x {{.RcvrType}}) {{.Method}}(y {{.RcvrType}}, {{.Arg2Name}} {{.ArgType}}) {{.ResultType}}
1287 `)
1288
1289 var unOpImm = templateOf("unaryOpImm", `
1290 // {{.Method}}{{.DocRest}}
1291 //
1292 // Asm: {{.AsmOp}}
1293 func (x {{.RcvrType}}) {{.Method}}({{.ImmName}} uint8) {{.ResultType}}
1294 `)
1295
1296 var binOpImm = templateOf("binaryOpImm", `
1297 // {{.Method}}{{.DocRest}}
1298 //
1299 // Asm: {{.AsmOp}}
1300 func (x {{.RcvrType}}) {{.Method}}({{.ImmName}} uint8, y {{.ArgType}}) {{.ResultType}}
1301 `)
1302
1303 var toMask = templateOf("toMask", `
1304 // ToMask translates {{.Article}} {{.From.Name}} vector to a {{.To.Name}} mask vector
1305 // zero becomes false, not-zero becomes true
1306 func (x {{.From.Name}}) ToMask() {{.To.Name}}
1307 `)
1308
1309 var fromMask = templateOf("fromMask", `
1310 // To{{.To.Name}} translates a {{.From.Name}} mask vector to {{.Article}} {{.To.Name}} int vector
1311 // false becomes zero, true becomes -1
1312 func (x {{.From.Name}}) To{{.To.Name}}() {{.To.Name}}
1313 `)
1314
1315 var maskMergeUnsigned = templateOf("maskMerge",
1316 `// Masked returns x but with elements zeroed where mask is false.
1317 func (x {{.Name}}) Masked(mask Mask{{.ElemSize}}x{{.Count}}) {{.Name}} {
1318 im := mask.ToInt{{.ElemSize}}x{{.Count}}().ToBits()
1319 return im.And(x)
1320 }
1321
1322 // IfElse returns x but with elements set to y where mask is false.
1323 func (x {{.Name}}) IfElse(mask Mask{{.ElemSize}}x{{.Count}}, y {{.Name}}) {{.Name}} {
1324 im := mask.ToInt{{.ElemSize}}x{{.Count}}().ToBits()
1325 return x.BitSelect(y, im)
1326 }
1327 `)
1328
1329 var maskMergeFloat = templateOf("maskMerge",
1330 `// Masked returns x but with elements zeroed where mask is false.
1331 func (x {{.Name}}) Masked(mask Mask{{.ElemSize}}x{{.Count}}) {{.Name}} {
1332 im := mask.ToInt{{.ElemSize}}x{{.Count}}().ToBits()
1333 return im.And(x.ToBits()).BitsToFloat{{.ElemSize}}()
1334 }
1335
1336 // IfElse returns x but with elements set to y where mask is false.
1337 func (x {{.Name}}) IfElse(mask Mask{{.ElemSize}}x{{.Count}}, y {{.Name}}) {{.Name}} {
1338 im := mask.ToInt{{.ElemSize}}x{{.Count}}().ToBits()
1339 ix := x.ToBits()
1340 iy := y.ToBits()
1341 return ix.BitSelect(iy, im).BitsToFloat{{.ElemSize}}()
1342 }
1343 `)
1344
1345 var maskMergeInt = templateOf("maskMergeInt",
1346 `// Masked returns x but with elements zeroed where mask is false.
1347 func (x {{.Name}}) Masked(mask Mask{{.ElemSize}}x{{.Count}}) {{.Name}} {
1348 im := mask.ToInt{{.ElemSize}}x{{.Count}}()
1349 return im.And(x)
1350 }
1351
1352 // IfElse returns x but with elements set to y where mask is false.
1353 func (x {{.Name}}) IfElse(mask Mask{{.ElemSize}}x{{.Count}}, y {{.Name}}) {{.Name}} {
1354 im := mask.ToInt{{.ElemSize}}x{{.Count}}()
1355 return x.BitSelect(y, im)
1356 }
1357 `)
1358
1359 var toString = templateOf("toString",
1360 `// String returns a string representation of SIMD vector x.
1361 func (x {{.Name}}) String() string {
1362 var s [{{.Count}}]{{.Elem}}
1363 x.StoreArray(&s)
1364 return sliceToString(s[:])
1365 }
1366 `)
1367
1368 var maskToString = templateOf("maskToString",
1369 `// String returns a string representation of SIMD mask x.
1370 func (x Mask{{.ElemSize}}x{{.Count}}) String() string {
1371 var s [{{.Count}}]{{.Elem}}
1372 x.ToInt{{.ElemSize}}x{{.Count}}().Neg().StoreArray(&s)
1373 return sliceToString(s[:])
1374 }
1375 `)
1376
1377 type asConversion struct {
1378 From, To *simdType
1379 Article string
1380 }
1381
1382 func forAllAsConversions(f func(from, to *simdType)) {
1383 for _, from := range allTypes {
1384 for _, to := range allTypes {
1385 if from == to {
1386 continue
1387 }
1388 f(from, to)
1389 }
1390 }
1391 }
1392
1393 func fromUnsignedToFloats(f func(from, to *simdType)) {
1394 for _, to := range floats {
1395 from := to.UintFor()
1396 f(from, to)
1397 }
1398 }
1399
1400 func fromUnsignedToInts(f func(from, to *simdType)) {
1401 for _, to := range signed {
1402 from := to.UintFor()
1403 f(from, to)
1404 }
1405 }
1406
1407 func forAllReshape(f func(from, to *simdType)) {
1408 for _, from := range unsigned {
1409 for _, to := range unsigned {
1410 if from == to {
1411 continue
1412 }
1413 f(from, to)
1414 }
1415 }
1416 }
1417
1418 func genOps() (f *bytes.Buffer) {
1419 f = new(bytes.Buffer)
1420 fmt.Fprintln(f, "// Code generated by 'wasmgen'; DO NOT EDIT.")
1421 fmt.Fprintln(f)
1422 fmt.Fprintln(f, "//go:build goexperiment.simd && wasm")
1423 fmt.Fprintln(f)
1424 fmt.Fprintln(f, "package archsimd")
1425 fmt.Fprintln(f)
1426
1427
1428 for _, op := range wasmOps {
1429 if op.OpFlags()&(IsLoad|IsStore|IsSplat) != 0 {
1430 continue
1431 }
1432
1433 if op.Method() == "" {
1434 continue
1435 }
1436
1437 if op.ImmRange() > 0 {
1438 switch op.ArgCount() {
1439 case 1:
1440 unOpImm.Execute(f, op)
1441 case 2:
1442 binOpImm.Execute(f, op)
1443 default:
1444 panic(fmt.Errorf("Unexpected arg count %d for %v", op.ArgCount(), op))
1445 }
1446
1447 } else {
1448 switch op.ArgCount() {
1449 case 1:
1450 unOp.Execute(f, op)
1451 case 2:
1452 binOp.Execute(f, op)
1453 case 3:
1454 ternOp.Execute(f, op)
1455 default:
1456 panic(fmt.Errorf("Unexpected arg count %d for %v", op.ArgCount(), op))
1457 }
1458 }
1459 }
1460
1461 for _, t := range signed {
1462
1463
1464
1465 toMask.Execute(f, &asConversion{t, t.MaskFor(), t.Article()})
1466 fromMask.Execute(f, &asConversion{t.MaskFor(), t, t.Article()})
1467 }
1468
1469
1470 for _, t := range allTypes {
1471 if t.Name[0] == 'I' {
1472 maskMergeInt.Execute(f, t)
1473 } else if t.Name[0] == 'F' {
1474 maskMergeFloat.Execute(f, t)
1475 } else {
1476 maskMergeUnsigned.Execute(f, t)
1477 }
1478 }
1479
1480
1481 for _, t := range allTypes {
1482 toString.Execute(f, t)
1483 }
1484
1485
1486 for _, t := range signed {
1487 maskToString.Execute(f, t)
1488 }
1489
1490 fromUnsignedToFloats(func(from, to *simdType) {
1491 sgutil.ToFloatsDcl.Execute(f, sgutil.Conversion(from, to))
1492 sgutil.ToBitsDcl.Execute(f, sgutil.Conversion(to, from))
1493 })
1494
1495 fromUnsignedToInts(func(from, to *simdType) {
1496 sgutil.ToIntsDcl.Execute(f, sgutil.Conversion(from, to))
1497 sgutil.ToBitsDcl.Execute(f, sgutil.Conversion(to, from))
1498 })
1499
1500 forAllReshape(func(from, to *simdType) {
1501 sgutil.ReshapeDcl.Execute(f, sgutil.Conversion(from, to))
1502 })
1503 return
1504 }
1505
1506
1507
1508 func genSSAOps() (f *bytes.Buffer) {
1509 f = new(bytes.Buffer)
1510 fmt.Fprintln(f, "// Code generated by 'wasmgen'; DO NOT EDIT.")
1511 fmt.Fprintln(f)
1512 fmt.Fprintln(f, "package main")
1513 fmt.Fprintln(f)
1514
1515 fmt.Fprintln(f)
1516 fmt.Fprintln(f, "func simdWasmOps(vload, vstore, v11, v21, v31, v11gp, v11fp32, v11fp64, v1gpv, v1fp32v, v1fp64v, gpv, fp32v, fp64v regInfo) []opData {")
1517 fmt.Fprintln(f, "\treturn []opData{")
1518
1519 done := make(map[string]string)
1520
1521 for _, op := range wasmOps {
1522 if op.Flag(NonSigned | EmulatedRule) {
1523
1524 continue
1525 }
1526 var toPrint string
1527 ssaWasmOp := op.SsaWasmOp()
1528
1529 if op.ImmRange() > 0 {
1530
1531 toPrint = fmt.Sprintf("\t\t{name: \"%s\", argLength: %d, reg: %s, asm: \"%s\", aux: \"UInt8\", typ: \"%s\"},\n", ssaWasmOp, op.ArgCount(), op.RegInfo(), op.AsmOp(), op.SsaResultType())
1532 } else if op.Flag(IsCommutative) {
1533 toPrint = fmt.Sprintf("\t\t{name: \"%s\", argLength: %d, reg: %s, asm: \"%s\", commutative: true, typ: \"%s\"},\n", ssaWasmOp, op.ArgCount(), op.RegInfo(), op.AsmOp(), op.SsaResultType())
1534 } else {
1535 toPrint = fmt.Sprintf("\t\t{name: \"%s\", argLength: %d, reg: %s, asm: \"%s\", typ: \"%s\"},\n", ssaWasmOp, op.ArgCount(), op.RegInfo(), op.AsmOp(), op.SsaResultType())
1536 }
1537 if old := done[ssaWasmOp]; old != "" {
1538 if old != toPrint {
1539 panic(fmt.Errorf("Second definition of SSA WASM Op %s differed: \nold: %s\nnew: %s\n", ssaWasmOp, old, toPrint))
1540 }
1541 continue
1542 }
1543 done[ssaWasmOp] = toPrint
1544 fmt.Fprint(f, toPrint)
1545
1546 }
1547
1548 fmt.Fprintln(f, "\t}")
1549 fmt.Fprintln(f, "}")
1550 return
1551 }
1552
1553
1554
1555
1556 func genSSARules() {
1557 f, err := os.Create(*genSSARulesFile)
1558 if err != nil {
1559 log.Fatal(err)
1560 }
1561 defer f.Close()
1562
1563 fmt.Fprintln(f, "// Code generated by 'wasmgen'; DO NOT EDIT.")
1564 fmt.Fprintln(f)
1565
1566 for _, op := range wasmOps {
1567
1568 if op.Flag(NonSigned) {
1569
1570 continue
1571 }
1572 g := op.SsaGenOp()
1573 if g == "" {
1574 continue
1575 }
1576 if op.T().IsMask() {
1577 continue
1578 }
1579 switch op.ArgCount() {
1580 case 1, 2, 3:
1581 wasmOp := op.SsaWasmOp()
1582 if op.Flag(IsShift) {
1583 t := op.T()
1584 elemSize := t.ElemSize
1585 if op.Op()[0] == 'S' {
1586 fmt.Fprintf(f, "(%s x d:(Const64 [c])) && uint64(c) < %d => (%s x (I64Const [c]))\n", g, elemSize, wasmOp)
1587 fmt.Fprintf(f, "(%s x d:(I64Const [c])) && uint64(c) < %d => (%s x d)\n", g, elemSize, wasmOp)
1588 if op.op != "shr_s" {
1589 fmt.Fprintf(f, "// TODO need to do 'shiftIsBounded' for WASM SIMD Shifts\n")
1590 fmt.Fprintf(f, "(%s x y) => (SelectV (%s x y) (%s x x) (I64LtU y (I64Const [%d])))\n", g, wasmOp, t.Methods["xor"].SsaWasmOp(), elemSize)
1591 } else {
1592
1593 fmt.Fprintf(f, "// TODO need to do 'shiftIsBounded' for WASM SIMD Shifts\n")
1594 fmt.Fprintf(f, "(%s x y) => (SelectV (%s x y) (%s x (I64Const [%d])) (I64LtU y (I64Const [%d])))\n", g, wasmOp, wasmOp, elemSize-1, elemSize)
1595 }
1596 } else {
1597 shl := t.UintFor().Methods["shl"].SsaWasmOp()
1598 shr := t.UintFor().Methods["shr_u"].SsaWasmOp()
1599 or := t.UintFor().Methods["or"].SsaWasmOp()
1600 if strings.Contains(op.Op(), "Left") {
1601 fmt.Fprintf(f, "(%s x y) => (%s (%s x y) (%s x (I64Sub (I64Const [%d]) y)))\n", g, or, shl, shr, elemSize)
1602 } else {
1603 fmt.Fprintf(f, "(%s x y) => (%s (%s x y) (%s x (I64Sub (I64Const [%d]) y)))\n", g, or, shr, shl, elemSize)
1604 }
1605 }
1606 continue
1607 }
1608 if op.Flag(NonSigned) {
1609
1610 wasmOp = op.T().IntShaped.Methods[op.op].SsaWasmOp()
1611 }
1612 fmt.Fprintf(f, "(%s ...) => (%s ...)\n", g, wasmOp)
1613 continue
1614 default:
1615 panic("Haven't figured out SSA rule for " + op.String())
1616 }
1617
1618 }
1619 }
1620
1621
1622
1623
1624 func genWasmSSA() (f *bytes.Buffer) {
1625 f = new(bytes.Buffer)
1626
1627 fmt.Fprintln(f, "// Code generated by 'wasmgen'; DO NOT EDIT.")
1628 fmt.Fprintln(f)
1629 fmt.Fprintln(f, "package wasm")
1630 fmt.Fprintln(f)
1631 fmt.Fprintln(f, "import (")
1632 fmt.Fprintln(f, "\t\"cmd/compile/internal/ssa\"")
1633 fmt.Fprintln(f, "\t\"cmd/compile/internal/ssagen\"")
1634 fmt.Fprintln(f, "\t\"cmd/internal/obj\"")
1635 fmt.Fprintln(f, "\t\"cmd/internal/obj/wasm\"")
1636 fmt.Fprintln(f, ")")
1637 fmt.Fprintln(f)
1638 fmt.Fprintln(f, "func ssaGenSIMDValue(s *ssagen.State, v *ssa.Value, extend bool) bool {")
1639 fmt.Fprintln(f, "\tswitch v.Op {")
1640
1641 const (
1642 NONE = iota
1643 IMM1_64
1644 IMM1_U
1645 IMM1_S
1646
1647 IMM2_32
1648 IMM2_64
1649
1650
1651 I32V
1652 I64V
1653 F32V
1654 F64V
1655
1656 V
1657 VV
1658 V32
1659 VVV
1660 OP
1661 DONE
1662 )
1663
1664 type classifiedOP struct {
1665 op *wasmOp
1666 class int
1667 }
1668 var ssagenWasmOps []classifiedOP
1669
1670
1671
1672
1673
1674 classify := func(op *wasmOp) int {
1675 if op.ImmRange() > 0 {
1676 switch op.ArgCount() {
1677 case 1:
1678 if op.T().ElemSize < 64 {
1679 switch op.T().Elem[0] {
1680 case 'u':
1681 return IMM1_U
1682 case 'i':
1683 return IMM1_S
1684 }
1685 }
1686 return IMM1_64
1687 case 2:
1688 if op.T().ElemSize < 64 && !op.T().Float {
1689 return IMM2_32
1690 }
1691 return IMM2_64
1692 }
1693 } else {
1694 switch op.ArgCount() {
1695 case 1:
1696 if op.Flag(IsSplat) {
1697 switch op.ArgType() {
1698 case "int8", "int16", "int32":
1699 return I32V
1700 case "int64":
1701 return I64V
1702 case "float32":
1703 return F32V
1704 case "float64":
1705 return F64V
1706 default:
1707 panic(fmt.Errorf("op %s has unexpected splat arg type", op.String()))
1708 }
1709 }
1710 return V
1711 case 2:
1712 if c := op.ArgType()[0]; c == 'i' || c == 'u' {
1713 return V32
1714 } else {
1715 return VV
1716 }
1717 case 3:
1718 return VVV
1719 default:
1720 return OP
1721 }
1722 }
1723 panic(fmt.Errorf("op %s has class NONE", op.String()))
1724 }
1725
1726 done := make(map[string]bool)
1727
1728 for _, op := range wasmOps {
1729 if op.Flag(NonSigned | EmulatedRule) {
1730
1731 continue
1732 }
1733 if done[op.SsaWasmOp()] {
1734 continue
1735 }
1736 done[op.SsaWasmOp()] = true
1737 ssagenWasmOps = append(ssagenWasmOps, classifiedOP{op, classify(op)})
1738 }
1739
1740 slices.SortFunc(ssagenWasmOps, func(a, b classifiedOP) int {
1741 if c := a.class - b.class; c != 0 {
1742 return c
1743 }
1744 return compareWasmOps(a.op, b.op)
1745 })
1746
1747 ssagenWasmOps = append(ssagenWasmOps, classifiedOP{nil, DONE})
1748
1749 lastClass := NONE
1750 lastCR := 0
1751 for i, op := range ssagenWasmOps {
1752 if op.class != lastClass {
1753 lastCR = i
1754
1755 switch lastClass {
1756 case NONE:
1757 case IMM1_64:
1758 fmt.Fprintf(f,
1759 ` getValue128(s, v.Args[0])
1760 p := s.Prog(v.Op.Asm())
1761 p.To = obj.Addr{Type: obj.TYPE_CONST, Offset: v.AuxInt}
1762 `)
1763 case IMM1_U:
1764 fmt.Fprintf(f,
1765 ` getValue128(s, v.Args[0])
1766 p := s.Prog(v.Op.Asm())
1767 p.To = obj.Addr{Type: obj.TYPE_CONST, Offset: v.AuxInt}
1768 `)
1769 fmt.Fprintf(f, "\tif extend {\n\t\ts.Prog(wasm.AI64ExtendI32U)\n\t}\n")
1770
1771 case IMM1_S:
1772 fmt.Fprintf(f,
1773 ` getValue128(s, v.Args[0])
1774 p := s.Prog(v.Op.Asm())
1775 p.To = obj.Addr{Type: obj.TYPE_CONST, Offset: v.AuxInt}
1776 `)
1777 fmt.Fprintf(f, "\tif extend {\n\t\ts.Prog(wasm.AI64ExtendI32S)\n\t}\n")
1778
1779 case IMM2_32:
1780 fmt.Fprintf(f,
1781 ` getValue128(s, v.Args[0])
1782 getValue32(s, v.Args[1])
1783 p := s.Prog(v.Op.Asm())
1784 p.To = obj.Addr{Type: obj.TYPE_CONST, Offset: v.AuxInt}
1785 `)
1786 case IMM2_64:
1787 fmt.Fprintf(f,
1788 ` getValue128(s, v.Args[0])
1789 getValue64(s, v.Args[1])
1790 p := s.Prog(v.Op.Asm())
1791 p.To = obj.Addr{Type: obj.TYPE_CONST, Offset: v.AuxInt}
1792 `)
1793
1794 case I32V:
1795 fmt.Fprintf(f,
1796 ` getValue32(s, v.Args[0])
1797 s.Prog(v.Op.Asm())
1798 `)
1799 case I64V:
1800 fmt.Fprintf(f,
1801 ` getValue64(s, v.Args[0])
1802 s.Prog(v.Op.Asm())
1803 `)
1804 case F32V:
1805 fmt.Fprintf(f,
1806 ` getValueFxx(s, v.Args[0])
1807 s.Prog(v.Op.Asm())
1808 `)
1809 case F64V:
1810 fmt.Fprintf(f,
1811 ` getValueFxx(s, v.Args[0])
1812 s.Prog(v.Op.Asm())
1813 `)
1814
1815 case V:
1816 fmt.Fprintf(f,
1817 ` getValue128(s, v.Args[0])
1818 s.Prog(v.Op.Asm())
1819 `)
1820 case VV:
1821 fmt.Fprintf(f,
1822 ` getValue128(s, v.Args[0])
1823 getValue128(s, v.Args[1])
1824 s.Prog(v.Op.Asm())
1825 `)
1826 case V32:
1827
1828 fmt.Fprintf(f,
1829 ` getValue128(s, v.Args[0])
1830 getValue32(s, v.Args[1])
1831 s.Prog(v.Op.Asm())
1832 `)
1833 case VVV:
1834 fmt.Fprintf(f,
1835 ` getValue128(s, v.Args[0])
1836 getValue128(s, v.Args[1])
1837 getValue128(s, v.Args[2])
1838 s.Prog(v.Op.Asm())
1839 `)
1840 case OP:
1841 fmt.Fprintf(f,
1842 ` s.Prog(v.Op.Asm())
1843 `)
1844 }
1845 if op.class == DONE {
1846 fmt.Fprintln(f, `
1847 default:
1848 return false
1849 }
1850 return true
1851 }`)
1852 return
1853 }
1854
1855 fmt.Fprint(f, "case ")
1856 lastCR = i - 1
1857 lastClass = op.class
1858 }
1859
1860 sep := ","
1861 if op.class != ssagenWasmOps[i+1].class {
1862 sep = ":"
1863 }
1864 fmt.Fprintf(f, "ssa.OpWasm%s%s", op.op.SsaWasmOp(), sep)
1865 if i >= lastCR+3 {
1866 fmt.Fprintln(f)
1867 lastCR = i
1868 }
1869 }
1870 return f
1871 }
1872
1873
1874
1875
1876 func genGenerics() *bytes.Buffer {
1877 var newOps []sgutil.GenericOpsData
1878
1879 for _, op := range wasmOps {
1880 if op.SsaGenOp() == "" {
1881 continue
1882 }
1883 if !op.DefinesGeneric() {
1884 continue
1885 }
1886 newOp := sgutil.GenericOpsData{
1887 OpName: op.SsaGenOp(),
1888 OpInLen: op.ArgCount(),
1889 Comm: op.Flag(IsCommutative),
1890 HasAux: op.ImmRange() > 0,
1891 }
1892 newOps = append(newOps, newOp)
1893 }
1894
1895 buf := sgutil.MergeSIMDGenericOps(newOps, *genGenOpsFile, "wasm")
1896
1897 return buf
1898 }
1899
1900
1901
1902 func genIntrinsics() (f *bytes.Buffer) {
1903 f = new(bytes.Buffer)
1904
1905 fmt.Fprint(f, `// Code generated by 'wasmgen'; DO NOT EDIT.
1906
1907 package ssagen
1908
1909 import (
1910 "cmd/compile/internal/ir"
1911 "cmd/compile/internal/ssa"
1912 "cmd/compile/internal/types"
1913 "cmd/internal/sys"
1914 )
1915
1916 func initWasmSIMD() {
1917 makeSimdOp1 := func(op ssa.Op) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
1918 return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
1919 return s.newValue1(op, types.TypeVec128, args[0])
1920 }
1921 }
1922 makeSimdOp2 := func(op ssa.Op) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
1923 return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
1924 return s.newValue2(op, types.TypeVec128, args[0], args[1])
1925 }
1926 }
1927 makeSimdOp3 := func(op ssa.Op) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
1928 return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
1929 return s.newValue3(op, types.TypeVec128, args[0], args[1], args[2])
1930 }
1931 }
1932
1933 // "As" is a type pun, just return the bits
1934 makeAsOp := func() func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
1935 return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
1936 return args[0]
1937 }
1938 }
1939
1940 // converting to a mask is an not-equals comparison with zero, zero obtained by x XOR x.
1941 makeToMask := func(op, xor ssa.Op) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
1942 return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
1943 return s.newValue2(op, types.TypeVec128, args[0], s.newValue2(xor, n.Type(), args[0], args[0]))
1944 }
1945 }
1946
1947 makeSimdOp1Imm8 := func(op ssa.Op, immLimit uint64) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
1948 return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
1949 t := n.Type()
1950 if args[1].Op == ssa.OpConst8 {
1951 return s.newValue1I(op, t, args[1].AuxInt, args[0])
1952 }
1953 return immJumpTableN(s, args[1], n, immLimit, func(sNew *state, idx int) {
1954 // Encode as int8 due to requirement of AuxInt, check its comment for details.
1955 s.vars[n] = sNew.newValue1I(op, t, int64(int8(idx)), args[0])
1956 })
1957 }
1958 }
1959
1960 makeSimdOp2Imm8 := func(op ssa.Op, immLimit uint64) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
1961 return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
1962 t := types.TypeVec128
1963 if args[1].Op == ssa.OpConst8 {
1964 return s.newValue2I(op, t, args[1].AuxInt, args[0], args[2])
1965 }
1966 return immJumpTableN(s, args[1], n, immLimit, func(sNew *state, idx int) {
1967 // Encode as int8 due to requirement of AuxInt, check its comment for details.
1968 s.vars[n] = sNew.newValue2I(op, t, int64(int8(idx)), args[0], args[2])
1969 })
1970 }
1971 }
1972
1973 addWasmSIMD := func(pkg, fn string, builder func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value) {
1974 intrinsics.add(sys.ArchWasm, pkg, fn, builder)
1975 }
1976
1977 `)
1978
1979 for _, op := range wasmOps {
1980
1981 typeName := op.RcvrType()
1982 funcName := op.Method()
1983
1984
1985
1986
1987
1988 fullname := typeName + "." + funcName
1989
1990 g := op.SsaGenOp()
1991 if g == "" || op.Flag(IsSplat) {
1992 continue
1993 }
1994 genOp := "ssa.Op" + g
1995
1996 if op.ImmRange() > 0 {
1997 switch op.ArgCount() {
1998 case 1:
1999 fmt.Fprintf(f, "\taddWasmSIMD(\"%s\", \"%s\", makeSimdOp1Imm8(%s, %d))\n", pkg, fullname, genOp, op.ImmRange())
2000 case 2:
2001 fmt.Fprintf(f, "\taddWasmSIMD(\"%s\", \"%s\", makeSimdOp2Imm8(%s, %d))\n", pkg, fullname, genOp, op.ImmRange())
2002 default:
2003 panic("unexpected wasm simd intrinsic " + op.String())
2004 }
2005 } else {
2006 switch op.ArgCount() {
2007 case 1:
2008 fmt.Fprintf(f, "\taddWasmSIMD(\"%s\", \"%s\", makeSimdOp1(%s))\n", pkg, fullname, genOp)
2009 case 2:
2010 fmt.Fprintf(f, "\taddWasmSIMD(\"%s\", \"%s\", makeSimdOp2(%s))\n", pkg, fullname, genOp)
2011 case 3:
2012 fmt.Fprintf(f, "\taddWasmSIMD(\"%s\", \"%s\",makeSimdOp3(%s))\n", pkg, fullname, genOp)
2013 default:
2014 panic("unexpected wasm simd intrinsic " + op.String())
2015 }
2016 }
2017
2018 }
2019
2020 for _, t := range signed {
2021
2022
2023
2024 fmt.Fprintf(f, "\taddWasmSIMD(\"%s\", \"%s\", makeToMask(%s, %s))\n", pkg, t.Name+".ToMask", "ssa.Op"+t.Methods["ne"].SsaGenOp(), "ssa.Op"+t.Methods["xor"].SsaGenOp())
2025 fmt.Fprintf(f, "\taddWasmSIMD(\"%s\", \"%s\", makeAsOp())\n", pkg, t.MaskFor().Name+".To"+t.Name)
2026 }
2027
2028
2029 for _, t := range allTypes {
2030 u := t
2031 if t.Unsigned {
2032 u = t.IntShaped
2033 }
2034 fmt.Fprintf(f, "\taddWasmSIMD(\"%s\", \"%s\", simdLoad())\n", pkg, "Load"+t.Name+"Array")
2035 fmt.Fprintf(f, "\taddWasmSIMD(\"%s\", \"%s\", simdBroadcast(ssa.OpBroadcast%s))\n", pkg, "Broadcast"+t.Name, u.Name)
2036 fmt.Fprintf(f, "\taddWasmSIMD(\"%s\", \"%s\", simdStore())\n", pkg, t.Name+".StoreArray")
2037 }
2038
2039
2040 forAllAsConversions(func(from, to *simdType) {
2041 fullname := from.Name + ".As" + to.Name
2042 fmt.Fprintf(f, "\taddWasmSIMD(\"%s\", \"%s\",makeAsOp())\n", pkg, fullname)
2043 })
2044
2045 var WasmTypeDotMethodIntrinsic = templateOf("wasm bit pun intrinsic", `addWasmSIMD("`+pkg+`", "{{.TypeDotMethod}}", makeAsOp())
2046 `)
2047
2048
2049
2050 fromUnsignedToFloats(func(from, to *simdType) {
2051 sgutil.Conversion(from, to).ExecuteIntrinsicTemplateOfTypeDotMethod(f, WasmTypeDotMethodIntrinsic)
2052 })
2053
2054 fromUnsignedToInts(func(from, to *simdType) {
2055 sgutil.Conversion(from, to).ExecuteIntrinsicTemplateOfTypeDotMethod(f, WasmTypeDotMethodIntrinsic)
2056 })
2057
2058 forAllReshape(func(from, to *simdType) {
2059 sgutil.Conversion(from, to).ExecuteIntrinsicTemplateOfTypeDotMethod(f, WasmTypeDotMethodIntrinsic)
2060 })
2061
2062 fmt.Fprintln(f, "}")
2063 return
2064 }
2065
View as plain text