1
2
3
4
5 package sve
6
7 import (
8 "encoding/xml"
9 "reflect"
10 "strings"
11 "testing"
12
13 "simd/archsimd/_gen/unify"
14
15 "golang.org/x/arch/arm64/instgen/xmlspec"
16 )
17
18
19
20 const sizeTable = `
21 <explanations>
22 <explanation>
23 <symbol link="t"><T></symbol>
24 <definition>
25 <table><tgroup><tbody>
26 <row><entry class="symbol">B</entry></row>
27 <row><entry class="symbol">H</entry></row>
28 <row><entry class="symbol">S</entry></row>
29 <row><entry class="symbol">D</entry></row>
30 </tbody></tgroup></table>
31 </definition>
32 </explanation>
33 </explanations>`
34
35
36 const addUnpred = `<instructionsection id="add_z_zz" title="ADD (vectors, unpredicated)" type="instruction">
37 <docvars>
38 <docvar key="instr-class" value="sve"/>
39 <docvar key="mnemonic" value="ADD"/>
40 </docvars>
41 <desc><authored><para>Add active elements of the second source to the first.</para></authored></desc>
42 <classes><iclass><encoding name="add_z_zz">
43 <asmtemplate><text>ADD </text><a link="zd"><Zd></a><text>.</text><a link="t"><T></a><text>, </text><a link="zn"><Zn></a><text>.</text><a link="t"><T></a><text>, </text><a link="zm"><Zm></a><text>.</text><a link="t"><T></a></asmtemplate>
44 </encoding></iclass></classes>` + sizeTable + `</instructionsection>`
45
46
47
48 const addPred = `<instructionsection id="add_z_p_zz" title="ADD (vectors, predicated)" type="instruction">
49 <docvars>
50 <docvar key="instr-class" value="sve"/>
51 <docvar key="mnemonic" value="ADD"/>
52 </docvars>
53 <classes><iclass><encoding name="add_z_p_zz">
54 <asmtemplate><text>ADD </text><a link="zdn"><Zdn></a><text>.</text><a link="t"><T></a><text>, </text><a link="pg"><Pg></a><text>/M, </text><a link="zdn"><Zdn></a><text>.</text><a link="t"><T></a><text>, </text><a link="zm"><Zm></a><text>.</text><a link="t"><T></a></asmtemplate>
55 </encoding></iclass></classes>` + sizeTable + `</instructionsection>`
56
57
58
59 const faddUnpred = `<instructionsection id="fadd_z_zz" title="FADD (vectors, unpredicated)" type="instruction">
60 <desc><brief><para>Floating-point add (unpredicated)</para></brief></desc>
61 <docvars>
62 <docvar key="instr-class" value="sve"/>
63 <docvar key="mnemonic" value="FADD"/>
64 </docvars>
65 <classes><iclass><encoding name="fadd_z_zz">
66 <asmtemplate><text>FADD </text><a link="zd"><Zd></a><text>.</text><a link="t"><T></a><text>, </text><a link="zn"><Zn></a><text>.</text><a link="t"><T></a><text>, </text><a link="zm"><Zm></a><text>.</text><a link="t"><T></a></asmtemplate>
67 </encoding></iclass></classes>
68 <explanations>
69 <explanation>
70 <symbol link="t"><T></symbol>
71 <definition><table><tgroup><tbody>
72 <row><entry class="symbol">H</entry></row>
73 <row><entry class="symbol">S</entry></row>
74 <row><entry class="symbol">D</entry></row>
75 </tbody></tgroup></table></definition>
76 </explanation>
77 </explanations>
78 </instructionsection>`
79
80 func parse(t *testing.T, x string) *Instruction {
81 t.Helper()
82 var ip xmlspec.InstructionParsed
83 if err := xml.Unmarshal([]byte(x), &ip); err != nil {
84 t.Fatalf("unmarshal: %v", err)
85 }
86 return &Instruction{Instruction: ip.Instruction}
87 }
88
89
90
91 func briefInst(t *testing.T, mnemonic, brief string) *Instruction {
92 t.Helper()
93 return parse(t, `<instructionsection id="x" title="x" type="instruction">
94 <desc><brief><para>`+brief+`</para></brief></desc>
95 <classes><iclass>
96 <docvars><docvar key="instr-class" value="sve"/><docvar key="mnemonic" value="`+mnemonic+`"/></docvars>
97 </iclass></classes>
98 </instructionsection>`)
99 }
100
101 func TestSignedness(t *testing.T) {
102 cases := []struct{ mn, brief, want string }{
103
104 {"ADD", "Add (predicated)", ""},
105 {"MUL", "Multiply (unpredicated)", ""},
106 {"EOR", "Bitwise exclusive-OR (predicated)", ""},
107
108 {"DUP", "Move signed integer immediate to vector elements", ""},
109
110 {"CLS", "Count leading sign bits (predicated)", ""},
111
112 {"SMAX", "Signed maximum (predicated)", "int"},
113 {"UMAX", "Unsigned maximum (predicated)", "uint"},
114 {"SQDMULH", "Signed saturating doubling multiply high (unpredicated)", "int"},
115 {"SCVTF", "Signed integer convert to floating-point (predicated)", "int"},
116
117 {"ASR", "Arithmetic shift right (predicated)", "int"},
118 {"LSR", "Logical shift right (predicated)", "uint"},
119 {"FLOGB", "Floating-point base 2 logarithm as integer (predicated)", "int"},
120 }
121 for _, c := range cases {
122 if got := briefInst(t, c.mn, c.brief).signedness(); got != c.want {
123 t.Errorf("%s (%q): signedness=%q, want %q", c.mn, c.brief, got, c.want)
124 }
125 }
126 }
127
128 func TestIsFloatBrief(t *testing.T) {
129 cases := []struct {
130 brief string
131 want bool
132 }{
133 {"Floating-point add (predicated)", true},
134 {"Double-precision convert to single-precision, rounding to odd", true},
135 {"Half-precision multiply-add to single-precision", true},
136 {"8-bit floating-point convert to BFloat16", true},
137 {"Add (predicated)", false},
138 {"Multiply (unpredicated)", false},
139 {"Scalar index of first true predicate element (predicated)", false},
140 {"Count leading sign bits (predicated)", false},
141 }
142 for _, c := range cases {
143 if got := isFloatBrief(c.brief); got != c.want {
144 t.Errorf("isFloatBrief(%q) = %v, want %v", c.brief, got, c.want)
145 }
146 }
147 }
148
149 func TestMnemonicAndClass(t *testing.T) {
150 inst := parse(t, addUnpred)
151 if got := inst.mnemonic(); got != "ADD" {
152 t.Errorf("mnemonic = %q, want ADD", got)
153 }
154 if !inst.isSVE() {
155 t.Errorf("isSVE = false, want true")
156 }
157 if got := inst.cpuFeature(); got != "SVE" {
158 t.Errorf("cpuFeature = %q, want SVE", got)
159 }
160 }
161
162 func bitsOf(rows []arngRow) []int {
163 var b []int
164 for _, r := range rows {
165 b = append(b, r.bits)
166 }
167 return b
168 }
169
170 func TestArrangements(t *testing.T) {
171 if got := bitsOf(parse(t, addUnpred).resolveArrangementTable("t")); !reflect.DeepEqual(got, []int{8, 16, 32, 64}) {
172 t.Errorf("ADD <T> domain = %v, want [8 16 32 64]", got)
173 }
174 if got := bitsOf(parse(t, faddUnpred).resolveArrangementTable("t")); !reflect.DeepEqual(got, []int{16, 32, 64}) {
175 t.Errorf("FADD <T> domain = %v, want [16 32 64]", got)
176 }
177 }
178
179 func TestOperands(t *testing.T) {
180 ops := parse(t, addUnpred).operands()
181 var got []string
182 for _, op := range ops {
183 got = append(got, op.Type.String()+":"+op.role)
184 }
185 want := []string{"ZReg:destination", "ZReg:op0", "ZReg:op1"}
186 if !reflect.DeepEqual(got, want) {
187 t.Errorf("operands = %v, want %v", got, want)
188 }
189 }
190
191 func TestEmitAllUnpredicated(t *testing.T) {
192
193 defs := parse(t, addUnpred).emitAll()
194 if len(defs) != 8 {
195 t.Fatalf("ADD emitAll = %d defs, want 8", len(defs))
196 }
197 s := defs[0].String()
198 for _, want := range []string{"ZADD", "arm64", "SVE", "elemBits"} {
199 if !strings.Contains(s, want) {
200 t.Errorf("emitted def missing %q:\n%s", want, s)
201 }
202 }
203
204
205 if got := len(parse(t, faddUnpred).emitAll()); got != 3 {
206 t.Errorf("FADD emitAll = %d defs, want 3", got)
207 }
208 }
209
210 func TestOperandsPredicated(t *testing.T) {
211
212
213
214 ops := parse(t, addPred).operands()
215 var got []string
216 for _, op := range ops {
217 got = append(got, op.Class+":"+op.role)
218 }
219 want := []string{"vreg:destination", "mask:mask", "vreg:op0", "vreg:op1"}
220 if !reflect.DeepEqual(got, want) {
221 t.Errorf("predicated operands = %v, want %v", got, want)
222 }
223 if !ops[0].resultInArg0() {
224 t.Errorf("expected <Zdn> destination to be result-in-arg0")
225 }
226 }
227
228
229
230 func maskPredications(t *testing.T, d *unify.Value) []string {
231 t.Helper()
232 var op struct {
233 In []struct {
234 Class string
235 Predication *string
236 } `unify:"in"`
237 }
238 if err := d.Decode(&op); err != nil {
239 t.Fatal(err)
240 }
241 var got []string
242 for _, in := range op.In {
243 if in.Class == "mask" && in.Predication != nil {
244 got = append(got, *in.Predication)
245 }
246 }
247 return got
248 }
249
250 func TestEmitAllPredicated(t *testing.T) {
251
252
253
254 defs := parse(t, addPred).emitAll()
255 if len(defs) != 8 {
256 t.Fatalf("predicated ADD emitAll = %d defs, want 8", len(defs))
257 }
258 for _, d := range defs {
259 if got := maskPredications(t, d); !reflect.DeepEqual(got, []string{"M"}) {
260 t.Errorf("want one mask input with predication M, got %v", got)
261 }
262 }
263 }
264
265
266
267 const fabsMZ = `<instructionsection id="fabs_z_p_z" title="FABS -- A64" type="instruction">
268 <desc><brief><para>Floating-point absolute value (predicated)</para></brief></desc>
269 <classes><iclass>
270 <docvars><docvar key="instr-class" value="sve"/><docvar key="mnemonic" value="FABS"/></docvars>
271 <encoding name="fabs_z_p_z_m">
272 <asmtemplate><text>FABS </text><a link="zd"><Zd></a><text>.</text><a link="t"><T></a><text>, </text><a link="pg"><Pg></a><text>/M, </text><a link="zn"><Zn></a><text>.</text><a link="t"><T></a></asmtemplate>
273 </encoding>
274 <encoding name="fabs_z_p_z_z">
275 <asmtemplate><text>FABS </text><a link="zd"><Zd></a><text>.</text><a link="t"><T></a><text>, </text><a link="pg"><Pg></a><text>/Z, </text><a link="zn"><Zn></a><text>.</text><a link="t"><T></a></asmtemplate>
276 </encoding>
277 </iclass></classes>
278 <explanations><explanation>
279 <symbol link="t"><T></symbol>
280 <definition><table><tgroup><tbody>
281 <row><entry class="bitfield">01</entry><entry class="symbol">H</entry></row>
282 <row><entry class="bitfield">10</entry><entry class="symbol">S</entry></row>
283 <row><entry class="bitfield">11</entry><entry class="symbol">D</entry></row>
284 </tbody></tgroup></table></definition>
285 </explanation></explanations>
286 </instructionsection>`
287
288 func TestPredicationMergingAndZeroing(t *testing.T) {
289
290
291 defs := parse(t, fabsMZ).emitAll()
292 if len(defs) != 6 {
293 t.Fatalf("FABS emitAll = %d defs, want 6", len(defs))
294 }
295 seen := map[string]int{}
296 for _, d := range defs {
297 for _, p := range maskPredications(t, d) {
298 seen[p]++
299 }
300 }
301 if seen["M"] != 3 || seen["Z"] != 3 {
302 t.Errorf("want 3 M and 3 Z variants, got %v", seen)
303 }
304 }
305
306
307
308 const movprfxZM = `<instructionsection id="movprfx_z_p_z" title="MOVPRFX -- A64" type="instruction">
309 <desc><brief><para>Move prefix (predicated)</para></brief></desc>
310 <classes><iclass>
311 <docvars><docvar key="instr-class" value="sve"/><docvar key="mnemonic" value="MOVPRFX"/></docvars>
312 <encoding name="movprfx_z_p_z_">
313 <asmtemplate><text>MOVPRFX </text><a link="zd"><Zd></a><text>.</text><a link="t"><T></a><text>, </text><a link="pg"><Pg></a><text>/</text><a link="zm"><ZM></a><text>, </text><a link="zn"><Zn></a><text>.</text><a link="t"><T></a></asmtemplate>
314 </encoding>
315 </iclass></classes>
316 <explanations><explanation>
317 <symbol link="t"><T></symbol>
318 <definition><table><tgroup><tbody>
319 <row><entry class="bitfield">00</entry><entry class="symbol">B</entry></row>
320 <row><entry class="bitfield">01</entry><entry class="symbol">H</entry></row>
321 <row><entry class="bitfield">10</entry><entry class="symbol">S</entry></row>
322 <row><entry class="bitfield">11</entry><entry class="symbol">D</entry></row>
323 </tbody></tgroup></table></definition>
324 </explanation></explanations>
325 </instructionsection>`
326
327 func TestPredicationZM(t *testing.T) {
328
329 defs := parse(t, movprfxZM).emitAll()
330 if len(defs) != 16 {
331 t.Fatalf("MOVPRFX emitAll = %d defs, want 16", len(defs))
332 }
333 seen := map[string]int{}
334 for _, d := range defs {
335 for _, p := range maskPredications(t, d) {
336 seen[p]++
337 }
338 }
339 if seen["M"] != 8 || seen["Z"] != 8 {
340 t.Errorf("want 8 M and 8 Z variants, got %v", seen)
341 }
342 }
343
344
345
346
347 const sunpkhi = `<instructionsection id="sunpkhi_z_z" title="SUNPKHI -- A64" type="instruction">
348 <docvars>
349 <docvar key="instr-class" value="sve"/>
350 <docvar key="mnemonic" value="SUNPKHI"/>
351 </docvars>
352 <classes><iclass><encoding name="sunpkhi_z_z">
353 <asmtemplate><text>SUNPKHI </text><a link="zd"><Zd></a><text>.</text><a link="t"><T></a><text>, </text><a link="zn"><Zn></a><text>.</text><a link="tb"><Tb></a></asmtemplate>
354 </encoding></iclass></classes>
355 <explanations>
356 <explanation><symbol link="t"><T></symbol><definition><table><tgroup><tbody>
357 <row><entry class="bitfield">01</entry><entry class="symbol">H</entry></row>
358 <row><entry class="bitfield">10</entry><entry class="symbol">S</entry></row>
359 <row><entry class="bitfield">11</entry><entry class="symbol">D</entry></row>
360 </tbody></tgroup></table></definition></explanation>
361 <explanation><symbol link="tb"><Tb></symbol><definition><table><tgroup><tbody>
362 <row><entry class="bitfield">01</entry><entry class="symbol">B</entry></row>
363 <row><entry class="bitfield">10</entry><entry class="symbol">H</entry></row>
364 <row><entry class="bitfield">11</entry><entry class="symbol">S</entry></row>
365 </tbody></tgroup></table></definition></explanation>
366 </explanations>
367 </instructionsection>`
368
369 func TestNonUniformArrangement(t *testing.T) {
370
371
372 inst := parse(t, sunpkhi)
373 defs := inst.emitAll()
374 if len(defs) != 6 {
375 t.Fatalf("SUNPKHI emitAll = %d defs, want 6", len(defs))
376 }
377
378 sawWiden := false
379 for _, d := range defs {
380 var op struct {
381 In []struct{ ElemBits int } `unify:"in"`
382 Out []struct{ ElemBits int } `unify:"out"`
383 }
384 if err := d.Decode(&op); err != nil {
385 t.Fatalf("decode: %v", err)
386 }
387 if len(op.In) != 1 || len(op.Out) != 1 {
388 t.Fatalf("want 1 in + 1 out, got in=%d out=%d", len(op.In), len(op.Out))
389 }
390 if op.Out[0].ElemBits != 2*op.In[0].ElemBits {
391 t.Errorf("out elemBits %d, want 2×in elemBits %d", op.Out[0].ElemBits, op.In[0].ElemBits)
392 }
393 if op.Out[0].ElemBits == 16 && op.In[0].ElemBits == 8 {
394 sawWiden = true
395 }
396 }
397 if !sawWiden {
398 t.Errorf("expected an H<-B widening variant")
399 }
400 }
401
402
403
404 const saddv = `<instructionsection id="saddv_r_p_z" title="SADDV -- A64" type="instruction">
405 <desc><brief><para>Signed add reduction to scalar</para></brief></desc>
406 <classes><iclass>
407 <docvars><docvar key="instr-class" value="sve"/><docvar key="mnemonic" value="SADDV"/></docvars>
408 <encoding name="saddv_r_p_z_">
409 <asmtemplate><text>SADDV </text><a link="dd"><Dd></a><text>, </text><a link="pg"><Pg></a><text>, </text><a link="zn"><Zn></a><text>.</text><a link="t"><T></a></asmtemplate>
410 </encoding>
411 </iclass></classes>
412 <explanations><explanation>
413 <symbol link="t"><T></symbol>
414 <definition><table><tgroup><tbody>
415 <row><entry class="bitfield">00</entry><entry class="symbol">B</entry></row>
416 <row><entry class="bitfield">01</entry><entry class="symbol">H</entry></row>
417 <row><entry class="bitfield">10</entry><entry class="symbol">S</entry></row>
418 </tbody></tgroup></table></definition>
419 </explanation></explanations>
420 </instructionsection>`
421
422 func TestReductionOutput(t *testing.T) {
423 ops := parse(t, saddv).operands()
424 var got []string
425 for _, op := range ops {
426 got = append(got, op.Class+":"+op.role)
427 }
428
429 want := []string{"vreg:destination", "mask:mask", "vreg:op0"}
430 if !reflect.DeepEqual(got, want) {
431 t.Errorf("SADDV operands = %v, want %v", got, want)
432 }
433
434 for _, d := range parse(t, saddv).emitAll() {
435 var op struct {
436 Out []struct {
437 Class string
438 Bits string
439 Lanes string
440 } `unify:"out"`
441 }
442 if err := d.Decode(&op); err != nil {
443 t.Fatal(err)
444 }
445 if len(op.Out) != 1 || op.Out[0].Class != "vreg" || op.Out[0].Bits != "64" || op.Out[0].Lanes != "1" {
446 t.Errorf("SADDV out = %+v, want one vreg bits=64 lanes=1", op.Out)
447 }
448 }
449 }
450
451
452
453
454 const st1b = `<instructionsection id="st1b_z_p_bi" title="ST1B -- A64" type="instruction">
455 <desc><brief><para>Contiguous store bytes from vector (immediate index)</para></brief></desc>
456 <classes><iclass>
457 <docvars><docvar key="instr-class" value="sve"/><docvar key="mnemonic" value="ST1B"/></docvars>
458 <encoding name="st1b_z_p_bi_">
459 <asmtemplate><text>ST1B { </text><a link="zt"><Zt></a><text>.</text><a link="t"><T></a><text> }, </text><a link="pg"><Pg></a><text>, [</text><a link="xn"><Xn|SP></a><text>{, #</text><a link="imm"><imm></a><text>, MUL VL}]</text></asmtemplate>
460 </encoding>
461 </iclass></classes>
462 <explanations><explanation>
463 <symbol link="t"><T></symbol>
464 <definition><table><tgroup><tbody>
465 <row><entry class="bitfield">00</entry><entry class="symbol">B</entry></row>
466 <row><entry class="bitfield">01</entry><entry class="symbol">H</entry></row>
467 <row><entry class="bitfield">10</entry><entry class="symbol">S</entry></row>
468 <row><entry class="bitfield">11</entry><entry class="symbol">D</entry></row>
469 </tbody></tgroup></table></definition>
470 </explanation></explanations>
471 </instructionsection>`
472
473 func TestStoreReglist(t *testing.T) {
474 ops := parse(t, st1b).operands()
475 var got []string
476 for _, op := range ops {
477 got = append(got, op.Class+":"+op.role)
478 }
479
480
481
482
483 want := []string{"vreg:op0", "mask:mask", "mem:destination"}
484 if !reflect.DeepEqual(got, want) {
485 t.Errorf("ST1B operands = %v, want %v", got, want)
486 }
487 for _, d := range parse(t, st1b).emitAll() {
488 var op struct {
489 In []struct {
490 Class string
491 ListNumber *string
492 } `unify:"in"`
493 Out []struct{ Class string } `unify:"out"`
494 }
495 if err := d.Decode(&op); err != nil {
496 t.Fatal(err)
497 }
498 if len(op.Out) != 1 || op.Out[0].Class != "mem" {
499 t.Errorf("ST1B out = %+v, want one mem", op.Out)
500 }
501
502
503 for _, in := range op.In {
504 isList := in.ListNumber != nil
505 if want := in.Class == "vreg"; isList != want {
506 t.Errorf("in %q listNumber present = %v, want %v", in.Class, isList, want)
507 }
508 }
509 }
510 }
511
512 func TestMemoryOperandClassified(t *testing.T) {
513
514
515 ops := operands("LD1B <Zt>.<T>, <Pg>/Z, [<Xn|SP>, #<imm>, MUL VL]")
516 if !hasClass(ops, "mem") {
517 t.Fatalf("expected a mem operand, got %v", ops)
518 }
519 for _, op := range ops {
520 if op.Class == "vreg" && strings.Contains(op.regName, "Xn") {
521 t.Errorf("memory address misclassified as vreg: %+v", op)
522 }
523 }
524 }
525
View as plain text