// Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) // SPDX-License-Identifier: BSD-3-Clause package asm import ( "encoding/binary" "strings" "testing" "sourcedock.dev/petrbalvin/gasm-sdk/parser" ) // assembleArm64Words parses src, assembles it for arm64 and returns the // first function's body as little-endian instruction words. Every statement // must encode: a failure is the test's. func assembleArm64Words(t *testing.T, src string) []uint32 { t.Helper() f, errs := parser.Parse("ext_arm64.s", src) if len(errs) > 0 { t.Fatalf("parse: %v", errs) } img, err := AssembleFileARM64(f) if err != nil { t.Fatalf("assemble: %v", err) } if len(img.Funcs) != 1 { t.Fatalf("got %d functions, want 1", len(img.Funcs)) } body := img.Code[img.Funcs[0].Offset:][:img.Funcs[0].Size] if len(body)%4 != 0 { t.Fatalf("body is %d bytes, not a whole number of instructions", len(body)) } words := make([]uint32, 0, len(body)/4) for i := 0; i < len(body); i += 4 { words = append(words, binary.LittleEndian.Uint32(body[i:])) } return words } // assembleArm64SrcError parses and assembles src and returns the assembler's // error text. func assembleArm64SrcError(t *testing.T, src string) string { t.Helper() f, errs := parser.Parse("ext_arm64.s", src) if len(errs) > 0 { t.Fatalf("parse: %v", errs) } _, err := AssembleFileARM64(f) if err == nil { t.Fatal("assembled, want an error") } return err.Error() } const arm64ExtProbeHead = "#include \"textflag.h\"\nTEXT ·t(SB), NOSPLIT, $0\n" // TestArm64AssembleExtensionGolden drives the wired layer through the full // assembler: text in, instruction word out. Each want is the encoding the // ARM Architecture Reference Manual's field layout gives for the statement: // the fixed class word, the size field from the arrangement, and the // register and immediate fields in the class's own places. The arch-level // golden vectors in arch/arm64_ext_test.go pin the same bytes at the // metadata layer; these pin the text-to-bytes path. func TestArm64AssembleExtensionGolden(t *testing.T) { tests := []struct { stmt string want uint32 }{ // Unpredicated three-vector: Zn, Zm, Zd, one shared arrangement. {"ADD Z1.S, Z2.S, Z0.S", 0x04a20020}, {"ADD Z0.B, Z1.B, Z2.B", 0x04210002}, {"SUB Z31.D, Z30.D, Z29.D", 0x04fe07fd}, {"SQADD Z5.H, Z6.H, Z7.H", 0x046610a7}, {"UQADD Z8.S, Z9.S, Z10.S", 0x04a9150a}, {"SQSUB Z5.H, Z6.H, Z7.H", 0x046618a7}, {"UQSUB Z8.S, Z9.S, Z10.S", 0x04a91d0a}, {"MUL Z0.B, Z1.B, Z2.B", 0x04216002}, {"SMULH Z11.D, Z12.D, Z13.D", 0x04ec696d}, {"UMULH Z0.B, Z1.B, Z2.B", 0x04216c02}, // Governed destructive, merging: Zm, Pg/M, Zdn; the governing // predicate is a 3-bit field, so P0-P7 alone. {"ADD Z1.S, P0/M, Z0.S", 0x04800020}, {"SUBR Z1.S, P7/M, Z0.S", 0x04831c20}, {"MUL Z3.D, P2/M, Z5.D", 0x04d00865}, {"SUBR Z0.B, P5/M, Z31.B", 0x0403141f}, // Immediate classes: imm{, LSL #8}, Zdn. A bare multiple of 256 // derives the shift bit, the spelling the layer canonicalises. {"ADD $255, Z0.S", 0x25a0dfe0}, {"ADD $65280, Z0.H", 0x2560ffe0}, {"ADD $255, LSL #8, Z0.S", 0x25a0ffe0}, {"ADD $(255<<8), Z0.S", 0x25a0ffe0}, {"MUL $-128, Z0.B", 0x2530d000}, } for _, tt := range tests { words := assembleArm64Words(t, arm64ExtProbeHead+"\t"+tt.stmt+"\n\tRET\n") if len(words) != 2 { t.Fatalf("%s: got %d words, want the statement and RET", tt.stmt, len(words)) } if words[0] != tt.want { t.Errorf("%s:\n got %08x\n want %08x", tt.stmt, words[0], tt.want) } if words[1] != 0xd65f03c0 { t.Errorf("%s: RET encoded %08x", tt.stmt, words[1]) } } } // TestArm64AssembleExtensionRefusals pins the diagnostics a pinned statement // gets from the layer instead of a scalar path's register complaint. func TestArm64AssembleExtensionRefusals(t *testing.T) { tests := []struct { stmt string want string }{ {"ADD Z0, Z1.S, Z2.S", "carries no arrangement suffix"}, {"ADD Z33.S, Z1.S, Z2.S", "outside Z0-Z31"}, {"ADD Z1.S, P0/Z, Z0.S", "/M"}, {"ADD Z1.S, P9/M, Z0.S", "outside P0-P7"}, {"ADD $300, Z0.S", "immediate 300"}, {"ADD $255<<8, Z0.S", "not an immediate the layer can read"}, {"ADD Z1.S, P0/M, R0", "wants a scalable vector register"}, {"ADD Z1.S, P0/B, Z0.S", "not an extended-layer operand"}, {"MUL $200, Z0.B", "outside the signed 8-bit range"}, {"ADD Z1.S, LSL #8, Z0.S", "LSL belongs straight after an immediate"}, } for _, tt := range tests { got := assembleArm64SrcError(t, arm64ExtProbeHead+"\t"+tt.stmt+"\n\tRET\n") if !strings.Contains(got, tt.want) { t.Errorf("%s: error %q does not name %q", tt.stmt, got, tt.want) } } } // TestArm64AssembleExtensionPredicateGolden drives the predicate family // through the full assembler: corpus spellings in, corpus words out. The // want words are the same arm64sveenc.s lines the arch-level golden test // pins; these prove the text-to-bytes path parses the dot and slash // qualifiers, the general registers and the counter spelling the statements // write. func TestArm64AssembleExtensionPredicateGolden(t *testing.T) { tests := []struct { stmt string want uint32 }{ // The logical operations, .B alone, the governing predicate with the // zeroing qualifier in its dot spelling. {"PAND P4.B, P2.B, P1.Z, P14.B", 0x2504444e}, {"PANDS P4.B, P2.B, P1.Z, P14.B", 0x2544444e}, {"PBIC P4.B, P2.B, P1.Z, P14.B", 0x2504445e}, {"PEOR P4.B, P2.B, P1.Z, P14.B", 0x2504464e}, {"PNAND P4.B, P2.B, P1.Z, P14.B", 0x2584465e}, {"PORR P4.B, P2.B, P1.Z, P14.B", 0x2584444e}, // The slash qualifier spells the same operand the dot spelling does. {"PAND P4.B, P2.B, P1/Z, P14.B", 0x2504444e}, // The select and the breaks. {"PSEL P4.B, P2.B, P1, P14.B", 0x2504465e}, {"PBRKA P5.B, P9.Z, P2.B", 0x251064a2}, {"PBRKAS P5.B, P9.Z, P4.B", 0x255064a4}, {"PBRKN P4.B, P2.B, P1.Z, P4.B", 0x25184444}, {"PBRKPA P4.B, P2.B, P1.Z, P14.B", 0x2504c44e}, // The permutations carry the arrangement into the size field. {"PTRN1 P5.D, P4.D, P2.D", 0x05e55082}, {"PUZP2 P5.D, P4.D, P2.D", 0x05e54c82}, {"PZIP1 P5.H, P4.H, P2.H", 0x05654082}, // The singles and the first-fault group. {"PPFALSE P13.B", 0x2518e40d}, {"PPFIRST P5.B, P9, P5.B", 0x2558c125}, {"PPNEXT P5.D, P4, P5.D", 0x25d9c485}, {"PPTEST P14.B, P0", 0x2550c1c0}, {"PPUNPKHI P14.B, P0.H", 0x053141c0}, {"PRDFFR P13.B", 0x2519f00d}, {"PRDFFR P14.Z, P0.B", 0x2518f1c0}, {"PRDFFRS P14.Z, P0.B", 0x2558f1c0}, {"PWRFFR P13.B", 0x252891a0}, {"PREV P14.S, P13.S", 0x05b441cd}, {"SETFFR", 0x252c9000}, // The while compares: general registers in, a sized predicate out. {"PWHILEGE R2, R10, P10.H", 0x2562114a}, {"PWHILELT R2, R10, P10.H", 0x2562154a}, {"PWHILELS R2, R10, P10.B", 0x25221d5a}, {"PWHILERW R2, R10, P10.H", 0x2562315a}, } for _, tt := range tests { words := assembleArm64Words(t, arm64ExtProbeHead+"\t"+tt.stmt+"\n\tRET\n") if len(words) != 2 { t.Fatalf("%s: got %d words, want the statement and RET", tt.stmt, len(words)) } if words[0] != tt.want { t.Errorf("%s:\n got %08x\n want %08x", tt.stmt, words[0], tt.want) } if words[1] != 0xd65f03c0 { t.Errorf("%s: RET encoded %08x", tt.stmt, words[1]) } } } // TestArm64AssembleExtensionPredicateRefusals pins the diagnostics the // predicate statements get from the layer. func TestArm64AssembleExtensionPredicateRefusals(t *testing.T) { tests := []struct { stmt string want string }{ {"PAND P4.B, P2.B, P1.M, P14.B", "zeroing qualifier"}, {"PAND P4.B, P2.B, P9.Z, P14.B", "outside P0-P7"}, {"PAND P8.B, P2.B, P1.Z, P14.B", "outside P0-P7"}, {"PAND P4.S, P2.B, P1.Z, P14.B", "want .B"}, {"PBRKN P4.B, P2.B, P1.Z, P9.B", "same register Pdm"}, {"PBRKAS P5.B, P9.M, P4.B", "zeroing qualifier"}, {"PSEL P4.B, P2.B, P1.Z, P14.B", "takes no qualifier"}, {"PTRN1 P5.D, P4.S, P2.D", "want .D"}, {"PPFALSE P13.S", "want .B"}, {"PPTRUE P6.S", "predicate-as-counter register"}, {"PPTRUE PN6.S", "outside PN8-PN15"}, {"PWHILELT R2, R31, P10.H", "outside R0-R30"}, {"PWHILELT R2, R10, P10.Q", "no size encoding"}, {"SETFFR P0.B", "takes 0 operands"}, } for _, tt := range tests { got := assembleArm64SrcError(t, arm64ExtProbeHead+"\t"+tt.stmt+"\n\tRET\n") if !strings.Contains(got, tt.want) { t.Errorf("%s: error %q does not name %q", tt.stmt, got, tt.want) } } } // TestArm64AssembleExtensionPredicateLeavesScalarsAlone pins the non- // invasion promise across the new operand kinds: general-register // statements whose mnemonics the layer also carries for predicates keep // their scalar behaviour whenever no vector, predicate or counter operand // appears. func TestArm64AssembleExtensionPredicateLeavesScalarsAlone(t *testing.T) { tests := []struct { stmt string want uint32 }{ {"ADD R0, R1, R2", 0x8b000022}, {"SUB R0, R1, R2", 0xcb000022}, } for _, tt := range tests { words := assembleArm64Words(t, arm64ExtProbeHead+"\t"+tt.stmt+"\n\tRET\n") if len(words) != 2 { t.Fatalf("%s: got %d words, want the statement and RET", tt.stmt, len(words)) } if words[0] != tt.want { t.Errorf("%s:\n got %08x\n want %08x", tt.stmt, words[0], tt.want) } } } // TestArm64AssembleExtensionPredicateLabelOffsets proves pass 1 and pass 2 // agree on a function mixing the predicate family with the vector family: // PWHILELT (4 bytes) and SETFFR (4 bytes) shift the label by exactly the // words pass 2 lays down. func TestArm64AssembleExtensionPredicateLabelOffsets(t *testing.T) { src := arm64ExtProbeHead + ` PWHILELT R2, R10, P10.H loop: SETFFR B loop PPFALSE P13.B RET ` words := assembleArm64Words(t, src) want := []uint32{0x2562154a, 0x252c9000, 0x17ffffff, 0x2518e40d, 0xd65f03c0} if len(words) != len(want) { t.Fatalf("got %d words, want %d", len(words), len(want)) } for i := range want { if words[i] != want[i] { t.Errorf("word %d: got %08x, want %08x", i, words[i], want[i]) } } } // TestArm64AssembleExtensionLabelOffsets proves pass 1 and pass 2 agree on a // function that mixes the layer with ordinary statements: the label after an // SVE instruction lands on the 4 bytes the encoder laid down, and the branch // back to it encodes the distance in words. func TestArm64AssembleExtensionLabelOffsets(t *testing.T) { src := arm64ExtProbeHead + ` ADD Z0.S, Z1.S, Z2.S loop: ADD $255, Z0.S B loop MUL $-128, Z0.B RET ` words := assembleArm64Words(t, src) want := []uint32{0x04a10002, 0x25a0dfe0, 0x17ffffff, 0x2530d000, 0xd65f03c0} if len(words) != len(want) { t.Fatalf("got %d words, want %d", len(words), len(want)) } for i := range want { if words[i] != want[i] { t.Errorf("word %d: got %08x, want %08x", i, words[i], want[i]) } } } // TestArm64AssembleExtensionLeavesScalarsAlone pins the non-invasion // promise: statements whose operands the scalar paths already read keep // their exact encodings, scalar and NEON alike, with the layer wired in. func TestArm64AssembleExtensionLeavesScalarsAlone(t *testing.T) { tests := []struct { stmt string want uint32 }{ {"ADD R0, R1, R2", 0x8b000022}, {"ADD $255, R0", 0x9103fc00}, {"SUB R0, R1, R2", 0xcb000022}, } for _, tt := range tests { words := assembleArm64Words(t, arm64ExtProbeHead+"\t"+tt.stmt+"\n\tRET\n") if len(words) != 2 { t.Fatalf("%s: got %d words, want the statement and RET", tt.stmt, len(words)) } if words[0] != tt.want { t.Errorf("%s:\n got %08x\n want %08x", tt.stmt, words[0], tt.want) } } } // TestArm64AssembleExtensionStage2Golden drives the SVE2.1 Z-alias // statements through the full assembler: corpus text in, corpus word out. // The statements carry the spellings the layer reads: dot and slash // qualifiers, bare predicates, bare vectors, general registers and the // immediate-with-source alias. func TestArm64AssembleExtensionStage2Golden(t *testing.T) { tests := []struct { stmt string want uint32 }{ {"ZABS Z7.D, P4.M, Z13.D", 0x04d6b0ed}, {"ZABS Z7.D, P4.Z, Z13.D", 0x04c6b0ed}, // The destination read back encodes once, in the destination slot // alone: a non-zero Zdn must stay clear of the class bits. {"ZADD Z15.B, Z5.B, P3.M, Z5.B", 0x04000de5}, {"ZSUB Z20.D, Z13.D, P6.M, Z13.D", 0x04c11a8d}, {"ZADD Z15.B, Z0.B, P3.M, Z0.B", 0x04000de0}, {"ZADD Z7.D, Z23.D, Z13.D", 0x04e702ed}, {"ZAND Z15.B, Z0.B, P3.M, Z0.B", 0x041a0de0}, {"ZAND Z7.D, Z6.D, Z23.D", 0x042730d7}, {"ZBIC Z15.B, Z0.B, P3.M, Z0.B", 0x041b0de0}, {"ZBIC Z7.D, Z6.D, Z23.D", 0x04e730d7}, {"ZCLS Z7.D, P4.M, Z13.D", 0x04d8b0ed}, {"ZCLS Z7.D, P4.Z, Z13.D", 0x04c8b0ed}, {"ZCLZ Z7.D, P4.M, Z13.D", 0x04d9b0ed}, {"ZCLZ Z7.D, P4.Z, Z13.D", 0x04c9b0ed}, {"ZCOMPACT Z7.D, P4, Z13.D", 0x05e190ed}, {"ZCOMPACT Z7.D, P4, Z13.D", 0x05e190ed}, {"ZEOR Z15.B, Z0.B, P3.M, Z0.B", 0x04190de0}, {"ZEOR Z7.D, Z6.D, Z23.D", 0x04a730d7}, {"ZEXPAND Z7.D, P4, Z13.D", 0x05f190ed}, {"ZMOVPRFX Z7.D, P4.Z, Z21.D", 0x04d030f5}, {"ZMOVPRFX Z11, Z6", 0x0420bd66}, {"ZNEG Z7.D, P4.M, Z13.D", 0x04d7b0ed}, {"ZNEG Z7.D, P4.Z, Z13.D", 0x04c7b0ed}, {"ZNOT Z7.D, P4.M, Z13.D", 0x04deb0ed}, {"ZNOT Z7.D, P4.Z, Z13.D", 0x04ceb0ed}, {"ZORR Z15.B, Z0.B, P3.M, Z0.B", 0x04180de0}, {"ZORR Z7.D, Z6.D, Z23.D", 0x046730d7}, {"ZREV Z1.S, Z26.S", 0x05b8383a}, {"ZREVB Z7.D, P4.M, Z13.D", 0x05e490ed}, {"ZREVB Z7.D, P4.Z, Z13.D", 0x05e4b0ed}, {"ZREVD Z13.Q, P1.M, Z22.Q", 0x052e85b6}, {"ZREVD Z13.Q, P1.Z, Z22.Q", 0x052ea5b6}, {"ZREVH Z7.D, P4.M, Z13.D", 0x05e590ed}, {"ZREVH Z7.D, P4.Z, Z13.D", 0x05e5b0ed}, {"ZREVW Z13.D, P1.M, Z22.D", 0x05e685b6}, {"ZREVW Z13.D, P1.Z, Z22.D", 0x05e6a5b6}, {"ZSEL Z23.B, Z21.B, P14, Z2.B", 0x0537faa2}, {"ZSQABS Z7.D, P4.M, Z13.D", 0x44c8b0ed}, {"ZSQABS Z7.D, P4.Z, Z13.D", 0x44cab0ed}, {"ZSQNEG Z7.D, P4.M, Z13.D", 0x44c9b0ed}, {"ZSQNEG Z7.D, P4.Z, Z13.D", 0x44cbb0ed}, {"ZSUB Z15.B, Z0.B, P3.M, Z0.B", 0x04010de0}, {"ZSUB Z7.D, Z23.D, Z13.D", 0x04e706ed}, {"ZSUBR Z15.B, Z0.B, P3.M, Z0.B", 0x04030de0}, {"ZSUNPKHI Z15.B, Z0.H", 0x057139e0}, {"ZSUNPKLO Z15.B, Z0.H", 0x057039e0}, {"ZSXTB Z7.D, P4.M, Z13.D", 0x04d0b0ed}, {"ZSXTB Z7.D, P4.Z, Z13.D", 0x04c0b0ed}, {"ZSXTH Z7.D, P4.M, Z13.D", 0x04d2b0ed}, {"ZSXTH Z7.D, P4.Z, Z13.D", 0x04c2b0ed}, {"ZSXTW Z13.D, P1.M, Z22.D", 0x04d4a5b6}, {"ZSXTW Z13.D, P1.Z, Z22.D", 0x04c4a5b6}, {"ZTBX Z7.D, Z23.D, Z13.D", 0x05e72eed}, {"ZTBXQ Z7.D, Z23.D, Z13.D", 0x05e736ed}, {"ZTRN1 Z7.D, Z23.D, Z13.D", 0x05e772ed}, {"ZTRN1 Z7.Q, Z6.Q, Z23.Q", 0x05a718d7}, {"ZTRN2 Z7.D, Z23.D, Z13.D", 0x05e776ed}, {"ZTRN2 Z7.Q, Z6.Q, Z23.Q", 0x05a71cd7}, {"ZUUNPKHI Z15.B, Z0.H", 0x057339e0}, {"ZUUNPKLO Z15.B, Z0.H", 0x057239e0}, {"ZUXTB Z7.D, P4.M, Z13.D", 0x04d1b0ed}, {"ZUXTB Z7.D, P4.Z, Z13.D", 0x04c1b0ed}, {"ZUXTH Z7.D, P4.M, Z13.D", 0x04d3b0ed}, {"ZUXTH Z7.D, P4.Z, Z13.D", 0x04c3b0ed}, {"ZUXTW Z13.D, P1.M, Z22.D", 0x04d5a5b6}, {"ZUXTW Z13.D, P1.Z, Z22.D", 0x04c5a5b6}, {"ZUZP1 Z7.D, Z23.D, Z13.D", 0x05e76aed}, {"ZUZP1 Z7.Q, Z6.Q, Z23.Q", 0x05a708d7}, {"ZUZP2 Z7.D, Z23.D, Z13.D", 0x05e76eed}, {"ZUZP2 Z7.Q, Z6.Q, Z23.Q", 0x05a70cd7}, {"ZUZPQ1 Z7.D, Z23.D, Z13.D", 0x44c7eaed}, {"ZUZPQ2 Z7.D, Z23.D, Z13.D", 0x44c7eeed}, {"ZZIP1 Z7.D, Z23.D, Z13.D", 0x05e762ed}, {"ZZIP1 Z7.Q, Z6.Q, Z23.Q", 0x05a700d7}, {"ZZIP2 Z7.D, Z23.D, Z13.D", 0x05e766ed}, {"ZZIP2 Z7.Q, Z6.Q, Z23.Q", 0x05a704d7}, {"ZZIPQ1 Z7.D, Z23.D, Z13.D", 0x44c7e2ed}, {"ZZIPQ2 Z7.D, Z23.D, Z13.D", 0x44c7e6ed}, {"ZDUP R2, Z10.D", 0x05e0384a}, {"ZINSR R2, Z10.D", 0x05e4384a}, {"ZADD $6, Z7.D, Z7.D", 0x25e0c0c7}, {"ZSUB $6, Z7.D, Z7.D", 0x25e1c0c7}, {"ZSUBR $6, Z7.D, Z7.D", 0x25e3c0c7}, } for _, tt := range tests { words := assembleArm64Words(t, arm64ExtProbeHead+"\t"+tt.stmt+"\n\tRET\n") if len(words) != 2 { t.Fatalf("%s: got %d words, want the statement and RET", tt.stmt, len(words)) } if words[0] != tt.want { t.Errorf("%s:\n got %08x\n want %08x", tt.stmt, words[0], tt.want) } } } // TestArm64AssembleExtensionStage3Golden drives the stage-three families // through the full assembler: corpus text in, corpus word out. The // statements carry the spellings the layer reads: the read-back crypto // destinations, the ZR of the dropped counter results, the bare governing // predicates and the V destinations of the reductions. func TestArm64AssembleExtensionStage3Golden(t *testing.T) { tests := []struct { stmt string want uint32 }{ // The crypto family. {"ZADCLB Z7.D, Z23.D, Z13.D", 0x4547d2ed}, {"ZADCLT Z7.D, Z23.D, Z13.D", 0x4547d6ed}, {"ZSBCLB Z7.D, Z23.D, Z13.D", 0x45c7d2ed}, {"ZSBCLT Z7.D, Z23.D, Z13.D", 0x45c7d6ed}, {"ZRAX1 Z7.D, Z6.D, Z23.D", 0x4527f4d7}, {"ZSM4EKEY Z7.S, Z6.S, Z23.S", 0x4527f0d7}, {"ZSM4E Z7.S, Z6.S, Z6.S", 0x4523e0e6}, {"ZAESD Z7.B, Z6.B, Z6.B", 0x4522e4e6}, {"ZAESE Z7.B, Z6.B, Z6.B", 0x4522e0e6}, {"ZAESIMC Z11.B, Z11.B", 0x4520e40b}, {"ZAESMC Z11.B, Z11.B", 0x4520e00b}, // The terminators and counters. {"CTERMEQ ZR, R25", 0x25ff2320}, {"CTERMEQW ZR, R25", 0x25bf2320}, {"CTERMNE ZR, R25", 0x25ff2330}, {"CTERMNEW ZR, R25", 0x25bf2330}, {"CTERMEQ R0, R25", 0x25e02320}, {"PCNTP P2.B, P14, R2", 0x2520b842}, {"PFIRSTP P2.B, P14, R2", 0x2521b842}, {"PLASTP P2.B, P14, R2", 0x2522b842}, {"PDECP P14.S, ZR", 0x25ad89df}, {"PINCP P14.S, ZR", 0x25ac89df}, {"PSQDECP P14.S, ZR", 0x25aa8ddf}, {"PSQINCP P14.S, ZR", 0x25a88ddf}, {"PUQDECP P14.S, ZR", 0x25ab8ddf}, {"PUQINCP P14.S, ZR", 0x25a98ddf}, {"PUQDECPW P14.S, ZR", 0x25ab89df}, {"PUQINCPW P14.S, ZR", 0x25a989df}, {"PSQDECPW R8, P10.D, R8", 0x25ea8948}, {"PSQINCPW R8, P10.D, R8", 0x25e88948}, {"PWHILEGEW R2, R10, P10.H", 0x2562014a}, {"PWHILEGTW R2, R10, P10.H", 0x2562015a}, {"PWHILEHIW R2, R10, P10.H", 0x2562095a}, {"PWHILEHSW R2, R10, P10.H", 0x2562094a}, {"PWHILELEW R2, R10, P10.H", 0x2562055a}, {"PWHILELOW R2, R10, P10.H", 0x25620d4a}, {"PWHILELSW R2, R10, P10.H", 0x25620d5a}, {"PWHILELTW R2, R10, P10.H", 0x2562054a}, // The reductions. {"ZSADDVD Z6.B, P3, V2", 0x04002cc2}, {"ZUADDVD Z10.D, P3, V15", 0x04c12d4f}, {"ZANDVB Z6.B, P3, V2", 0x041a2cc2}, {"ZANDVH Z3.H, P1, V29", 0x045a247d}, {"ZANDVS Z17.S, P1, V27", 0x049a263b}, {"ZANDVD Z10.D, P3, V15", 0x04da2d4f}, {"ZEORVB Z6.B, P3, V2", 0x04192cc2}, {"ZEORVD Z10.D, P3, V15", 0x04d92d4f}, {"ZORVB Z6.B, P3, V2", 0x04182cc2}, {"ZORVD Z10.D, P3, V15", 0x04d82d4f}, {"ZSMAXVB Z6.B, P3, V2", 0x04082cc2}, {"ZSMAXVH Z3.H, P1, V29", 0x0448247d}, {"ZSMAXVS Z17.S, P1, V27", 0x0488263b}, {"ZSMAXVD Z10.D, P3, V15", 0x04c82d4f}, {"ZSMINVB Z6.B, P3, V2", 0x040a2cc2}, {"ZSMINVD Z10.D, P3, V15", 0x04ca2d4f}, {"ZUMAXVB Z6.B, P3, V2", 0x04092cc2}, {"ZUMAXVD Z10.D, P3, V15", 0x04c92d4f}, {"ZUMINVB Z6.B, P3, V2", 0x040b2cc2}, {"ZUMINVD Z10.D, P3, V15", 0x04cb2d4f}, {"ZFADDVH Z3.H, P1, V29", 0x6540247d}, {"ZFADDVS Z17.S, P1, V27", 0x6580263b}, {"ZFADDVD Z10.D, P3, V15", 0x65c02d4f}, {"ZFMAXNMVH Z3.H, P1, V29", 0x6544247d}, {"ZFMAXNMVD Z10.D, P3, V15", 0x65c42d4f}, {"ZFMAXVH Z3.H, P1, V29", 0x6546247d}, {"ZFMAXVD Z10.D, P3, V15", 0x65c62d4f}, {"ZFMINNMVH Z3.H, P1, V29", 0x6545247d}, {"ZFMINNMVD Z10.D, P3, V15", 0x65c52d4f}, {"ZFMINVH Z3.H, P1, V29", 0x6547247d}, {"ZFMINVD Z10.D, P3, V15", 0x65c72d4f}, {"ZFADDAH Z8.H, V15, P2, V15", 0x6558290f}, {"ZFADDAS Z26.S, V30, P7, V30", 0x65983f5e}, {"ZFADDAD Z9.D, V10, P2, V10", 0x65d8292a}, } for _, tt := range tests { words := assembleArm64Words(t, arm64ExtProbeHead+"\t"+tt.stmt+"\n\tRET\n") if len(words) != 2 { t.Fatalf("%s: got %d words, want the statement and RET", tt.stmt, len(words)) } if words[0] != tt.want { t.Errorf("%s:\n got %08x\n want %08x", tt.stmt, words[0], tt.want) } if words[1] != 0xd65f03c0 { t.Errorf("%s: RET encoded %08x", tt.stmt, words[1]) } } } // TestArm64AssembleExtensionStage3Refusals pins the diagnostics the // stage-three statements get from the layer: the read-back mismatches, the // locked arrangements, the dropped-result zero register and the narrow // governing predicates. func TestArm64AssembleExtensionStage3Refusals(t *testing.T) { tests := []struct { stmt string want string }{ {"ZSM4E Z7.S, Z6.S, Z5.S", "the same register Zd"}, {"ZSM4E Z7.D, Z6.D, Z6.D", "want .S"}, {"ZAESIMC Z11.B, Z12.B", "the same register Zd"}, {"ZADCLB Z7.S, Z23.S, Z13.S", "want .D"}, {"CTERMEQ RSP, R25", "wants ZR here"}, {"CTERMEQ R31, R25", "outside R0-R30"}, {"PCNTP P2.B, P14, R31", "outside R0-R30"}, {"PCNTP P2.Q, P14, R2", "no size encoding"}, {"PDECP P14.S, R0", "spelled ZR"}, {"PDECP P14.Q, ZR", "no size encoding"}, {"PSQDECPW R8, P10.D, R9", "the same register"}, {"ZSADDVD Z6.B, P9, V2", "outside P0-P7"}, {"ZSADDVD Z6.B, P3/M, V2", "takes no qualifier"}, {"ZSADDVD Z6.B, P3, V32", "outside V0-V31"}, {"ZSADDVD Z6.B, P3, R2", "wants a SIMD register"}, {"ZANDVB Z6.S, P3, V2", "want .B"}, {"ZFADDAD Z9.D, V10, P2, V11", "the same register Vd"}, {"PWHILEGEW R2, R10, P10.H", ""}, } for _, tt := range tests { if tt.want == "" { continue } got := assembleArm64SrcError(t, arm64ExtProbeHead+"\t"+tt.stmt+"\n\tRET\n") if !strings.Contains(got, tt.want) { t.Errorf("%s: error %q does not name %q", tt.stmt, got, tt.want) } } } // TestArm64AssembleExtensionGatherGolden drives the gather/scatter family // through the full assembler: every corpus line of the scope the layer // covers, corpus text in, corpus word out. The scope is the plain, // sign-extended and first-fault loads and the stores over the core // addressing modes: the two-general-register pair, the vector base with a // general offset, the extended 32-bit offsets, the immediate offset on a // vector base and the lone vector base of the stores. func TestArm64AssembleExtensionGatherGolden(t *testing.T) { tests := []struct { stmt string want uint32 }{ {"ZLD1B (R6)(R14), P4.Z, [Z13.B]", 0xa40651cd}, {"ZLD1B (R6)(R14), P4.Z, [Z13.H]", 0xa42651cd}, {"ZLD1B (R6)(R14), P4.Z, [Z13.S]", 0xa44651cd}, {"ZLD1B (R6)(R14), P4.Z, [Z13.D]", 0xa46651cd}, {"ZLD1B (Z10.D)(R19), P3.Z, [Z15.D]", 0xc44ace6f}, {"ZLD1B (Z4.S.UXTW)(R3), P3.Z, [Z4.S]", 0x84044c64}, {"ZLD1B (Z6.D)(R14), P4.Z, [Z13.D]", 0xc446d1cd}, {"ZLD1D (R6<<3)(R14), P4.Z, [Z13.D]", 0xa5e651cd}, {"ZLD1D (R6<<3)(R14), P4.Z, [Z13.Q]", 0xa58691cd}, {"ZLD1D (Z23.D<<3)(R24), P1.Z, [Z22.D]", 0xc5f7c716}, {"ZLD1D (Z10.D)(R19), P3.Z, [Z15.D]", 0xc5cace6f}, {"ZLD1D (Z6.D<<3)(R14), P4.Z, [Z13.D]", 0xc5e6d1cd}, {"ZLD1D (Z6.D)(R14), P4.Z, [Z13.D]", 0xc5c6d1cd}, {"ZLD1H (R6<<1)(R14), P4.Z, [Z13.H]", 0xa4a651cd}, {"ZLD1H (R6<<1)(R14), P4.Z, [Z13.S]", 0xa4c651cd}, {"ZLD1H (R6<<1)(R14), P4.Z, [Z13.D]", 0xa4e651cd}, {"ZLD1H (Z4.S.UXTW<<1)(R3), P3.Z, [Z4.S]", 0x84a44c64}, {"ZLD1H (Z23.D<<1)(R24), P1.Z, [Z22.D]", 0xc4f7c716}, {"ZLD1H (Z10.D)(R19), P3.Z, [Z15.D]", 0xc4cace6f}, {"ZLD1H (Z4.S.UXTW)(R3), P3.Z, [Z4.S]", 0x84844c64}, {"ZLD1H (Z6.D<<1)(R14), P4.Z, [Z13.D]", 0xc4e6d1cd}, {"ZLD1H (Z6.D)(R14), P4.Z, [Z13.D]", 0xc4c6d1cd}, {"ZLD1SB (R6)(R14), P4.Z, [Z13.H]", 0xa5c651cd}, {"ZLD1SB (R6)(R14), P4.Z, [Z13.S]", 0xa5a651cd}, {"ZLD1SB (R6)(R14), P4.Z, [Z13.D]", 0xa58651cd}, {"ZLD1SB (Z10.D)(R19), P3.Z, [Z15.D]", 0xc44a8e6f}, {"ZLD1SB (Z4.S.UXTW)(R3), P3.Z, [Z4.S]", 0x84040c64}, {"ZLD1SB (Z6.D)(R14), P4.Z, [Z13.D]", 0xc44691cd}, {"ZLD1SH (R6<<1)(R14), P4.Z, [Z13.S]", 0xa52651cd}, {"ZLD1SH (R6<<1)(R14), P4.Z, [Z13.D]", 0xa50651cd}, {"ZLD1SH (Z4.S.UXTW<<1)(R3), P3.Z, [Z4.S]", 0x84a40c64}, {"ZLD1SH (Z23.D<<1)(R24), P1.Z, [Z22.D]", 0xc4f78716}, {"ZLD1SH (Z10.D)(R19), P3.Z, [Z15.D]", 0xc4ca8e6f}, {"ZLD1SH (Z4.S.UXTW)(R3), P3.Z, [Z4.S]", 0x84840c64}, {"ZLD1SH (Z6.D<<1)(R14), P4.Z, [Z13.D]", 0xc4e691cd}, {"ZLD1SH (Z6.D)(R14), P4.Z, [Z13.D]", 0xc4c691cd}, {"ZLD1SW (R6<<2)(R14), P4.Z, [Z13.D]", 0xa48651cd}, {"ZLD1SW (Z23.D<<2)(R24), P1.Z, [Z22.D]", 0xc5778716}, {"ZLD1SW (Z10.D)(R19), P3.Z, [Z15.D]", 0xc54a8e6f}, {"ZLD1SW (Z6.D<<2)(R14), P4.Z, [Z13.D]", 0xc56691cd}, {"ZLD1SW (Z6.D)(R14), P4.Z, [Z13.D]", 0xc54691cd}, {"ZLD1W (R6<<2)(R14), P4.Z, [Z13.S]", 0xa54651cd}, {"ZLD1W (R6<<2)(R14), P4.Z, [Z13.D]", 0xa56651cd}, {"ZLD1W (R6<<2)(R14), P4.Z, [Z13.Q]", 0xa50691cd}, {"ZLD1W (Z4.S.UXTW<<2)(R3), P3.Z, [Z4.S]", 0x85244c64}, {"ZLD1W (Z23.D<<2)(R24), P1.Z, [Z22.D]", 0xc577c716}, {"ZLD1W (Z10.D)(R19), P3.Z, [Z15.D]", 0xc54ace6f}, {"ZLD1W (Z4.S.UXTW)(R3), P3.Z, [Z4.S]", 0x85044c64}, {"ZLD1W (Z6.D<<2)(R14), P4.Z, [Z13.D]", 0xc566d1cd}, {"ZLD1W (Z6.D)(R14), P4.Z, [Z13.D]", 0xc546d1cd}, {"ZLDFF1B (R6)(R14), P4.Z, [Z13.B]", 0xa40671cd}, {"ZLDFF1B (R6)(R14), P4.Z, [Z13.H]", 0xa42671cd}, {"ZLDFF1B (R6)(R14), P4.Z, [Z13.S]", 0xa44671cd}, {"ZLDFF1B (R6)(R14), P4.Z, [Z13.D]", 0xa46671cd}, {"ZLDFF1B (Z10.D)(R19), P3.Z, [Z15.D]", 0xc44aee6f}, {"ZLDFF1B (Z4.S.UXTW)(R3), P3.Z, [Z4.S]", 0x84046c64}, {"ZLDFF1B (Z6.D)(R14), P4.Z, [Z13.D]", 0xc446f1cd}, {"ZLDFF1D (R6<<3)(R14), P4.Z, [Z13.D]", 0xa5e671cd}, {"ZLDFF1D (Z23.D<<3)(R24), P1.Z, [Z22.D]", 0xc5f7e716}, {"ZLDFF1D (Z10.D)(R19), P3.Z, [Z15.D]", 0xc5caee6f}, {"ZLDFF1D (Z6.D<<3)(R14), P4.Z, [Z13.D]", 0xc5e6f1cd}, {"ZLDFF1D (Z6.D)(R14), P4.Z, [Z13.D]", 0xc5c6f1cd}, {"ZLDFF1H (R6<<1)(R14), P4.Z, [Z13.H]", 0xa4a671cd}, {"ZLDFF1H (R6<<1)(R14), P4.Z, [Z13.S]", 0xa4c671cd}, {"ZLDFF1H (R6<<1)(R14), P4.Z, [Z13.D]", 0xa4e671cd}, {"ZLDFF1H (Z4.S.UXTW<<1)(R3), P3.Z, [Z4.S]", 0x84a46c64}, {"ZLDFF1H (Z23.D<<1)(R24), P1.Z, [Z22.D]", 0xc4f7e716}, {"ZLDFF1H (Z10.D)(R19), P3.Z, [Z15.D]", 0xc4caee6f}, {"ZLDFF1H (Z4.S.UXTW)(R3), P3.Z, [Z4.S]", 0x84846c64}, {"ZLDFF1H (Z6.D<<1)(R14), P4.Z, [Z13.D]", 0xc4e6f1cd}, {"ZLDFF1H (Z6.D)(R14), P4.Z, [Z13.D]", 0xc4c6f1cd}, {"ZLDFF1SB (R6)(R14), P4.Z, [Z13.H]", 0xa5c671cd}, {"ZLDFF1SB (R6)(R14), P4.Z, [Z13.S]", 0xa5a671cd}, {"ZLDFF1SB (R6)(R14), P4.Z, [Z13.D]", 0xa58671cd}, {"ZLDFF1SB (Z10.D)(R19), P3.Z, [Z15.D]", 0xc44aae6f}, {"ZLDFF1SB (Z4.S.UXTW)(R3), P3.Z, [Z4.S]", 0x84042c64}, {"ZLDFF1SB (Z6.D)(R14), P4.Z, [Z13.D]", 0xc446b1cd}, {"ZLDFF1SH (R6<<1)(R14), P4.Z, [Z13.S]", 0xa52671cd}, {"ZLDFF1SH (R6<<1)(R14), P4.Z, [Z13.D]", 0xa50671cd}, {"ZLDFF1SH (Z4.S.UXTW<<1)(R3), P3.Z, [Z4.S]", 0x84a42c64}, {"ZLDFF1SH (Z23.D<<1)(R24), P1.Z, [Z22.D]", 0xc4f7a716}, {"ZLDFF1SH (Z10.D)(R19), P3.Z, [Z15.D]", 0xc4caae6f}, {"ZLDFF1SH (Z4.S.UXTW)(R3), P3.Z, [Z4.S]", 0x84842c64}, {"ZLDFF1SH (Z6.D<<1)(R14), P4.Z, [Z13.D]", 0xc4e6b1cd}, {"ZLDFF1SH (Z6.D)(R14), P4.Z, [Z13.D]", 0xc4c6b1cd}, {"ZLDFF1SW (R6<<2)(R14), P4.Z, [Z13.D]", 0xa48671cd}, {"ZLDFF1SW (Z23.D<<2)(R24), P1.Z, [Z22.D]", 0xc577a716}, {"ZLDFF1SW (Z10.D)(R19), P3.Z, [Z15.D]", 0xc54aae6f}, {"ZLDFF1SW (Z6.D<<2)(R14), P4.Z, [Z13.D]", 0xc566b1cd}, {"ZLDFF1SW (Z6.D)(R14), P4.Z, [Z13.D]", 0xc546b1cd}, {"ZLDFF1W (R6<<2)(R14), P4.Z, [Z13.S]", 0xa54671cd}, {"ZLDFF1W (R6<<2)(R14), P4.Z, [Z13.D]", 0xa56671cd}, {"ZLDFF1W (Z4.S.UXTW<<2)(R3), P3.Z, [Z4.S]", 0x85246c64}, {"ZLDFF1W (Z23.D<<2)(R24), P1.Z, [Z22.D]", 0xc577e716}, {"ZLDFF1W (Z10.D)(R19), P3.Z, [Z15.D]", 0xc54aee6f}, {"ZLDFF1W (Z4.S.UXTW)(R3), P3.Z, [Z4.S]", 0x85046c64}, {"ZLDFF1W (Z6.D<<2)(R14), P4.Z, [Z13.D]", 0xc566f1cd}, {"ZLDFF1W (Z6.D)(R14), P4.Z, [Z13.D]", 0xc546f1cd}, {"ZST1B [Z7.D], P4, (R21)(R7)", 0xe47550e7}, {"ZST1B [Z6.D], P4, (Z21.D.UXTW)(R7)", 0xe41590e6}, {"ZST1B [Z6.S], P4, (Z21.S.UXTW)(R7)", 0xe45590e6}, {"ZST1B [Z8.D], P3, (Z6.D)(RSP)", 0xe406afe8}, {"ZST1D [Z8.D], P3, (R6<<3)(RSP)", 0xe5e64fe8}, {"ZST1D [Z8.Q], P3, (R6<<3)(RSP)", 0xe5c64fe8}, {"ZST1D [Z6.D], P4, (Z21.D.UXTW<<3)(R7)", 0xe5b590e6}, {"ZST1D [Z6.D], P4, (Z21.D.UXTW)(R7)", 0xe59590e6}, {"ZST1D [Z8.D], P3, (Z6.D<<3)(RSP)", 0xe5a6afe8}, {"ZST1D [Z8.D], P3, (Z6.D)(RSP)", 0xe586afe8}, {"ZST1H [Z7.D], P4, (R21<<1)(R7)", 0xe4f550e7}, {"ZST1H [Z6.S], P4, (Z21.S.UXTW<<1)(R7)", 0xe4f590e6}, {"ZST1H [Z6.D], P4, (Z21.D.UXTW<<1)(R7)", 0xe4b590e6}, {"ZST1H [Z6.D], P4, (Z21.D.UXTW)(R7)", 0xe49590e6}, {"ZST1H [Z6.S], P4, (Z21.S.UXTW)(R7)", 0xe4d590e6}, {"ZST1H [Z8.D], P3, (Z6.D<<1)(RSP)", 0xe4a6afe8}, {"ZST1H [Z8.D], P3, (Z6.D)(RSP)", 0xe486afe8}, {"ZST1W [Z7.D], P4, (R21<<2)(R7)", 0xe57550e7}, {"ZST1W [Z8.Q], P3, (R6<<2)(RSP)", 0xe5064fe8}, {"ZST1W [Z6.S], P4, (Z21.S.UXTW<<2)(R7)", 0xe57590e6}, {"ZST1W [Z6.D], P4, (Z21.D.UXTW<<2)(R7)", 0xe53590e6}, {"ZST1W [Z6.D], P4, (Z21.D.UXTW)(R7)", 0xe51590e6}, {"ZST1W [Z6.S], P4, (Z21.S.UXTW)(R7)", 0xe55590e6}, {"ZST1W [Z8.D], P3, (Z6.D<<2)(RSP)", 0xe526afe8}, {"ZST1W [Z8.D], P3, (Z6.D)(RSP)", 0xe506afe8}, {"ZLD1B 6(Z7.S), P4.Z, [Z13.S]", 0x8426d0ed}, {"ZLD1B 6(Z7.D), P4.Z, [Z13.D]", 0xc426d0ed}, {"ZLD1D 8(Z14.D), P6.Z, [Z6.D]", 0xc5a1d9c6}, {"ZLD1H 6(Z7.S), P4.Z, [Z13.S]", 0x84a3d0ed}, {"ZLD1H 6(Z7.D), P4.Z, [Z13.D]", 0xc4a3d0ed}, {"ZLD1SB 6(Z7.S), P4.Z, [Z13.S]", 0x842690ed}, {"ZLD1SB 6(Z7.D), P4.Z, [Z13.D]", 0xc42690ed}, {"ZLD1SH 6(Z7.S), P4.Z, [Z13.S]", 0x84a390ed}, {"ZLD1SH 6(Z7.D), P4.Z, [Z13.D]", 0xc4a390ed}, {"ZLD1SW 12(Z0.D), P6.Z, [Z25.D]", 0xc5239819}, {"ZLD1W 12(Z0.S), P6.Z, [Z25.S]", 0x8523d819}, {"ZLD1W 12(Z0.D), P6.Z, [Z25.D]", 0xc523d819}, {"ZLDFF1B 6(Z7.S), P4.Z, [Z13.S]", 0x8426f0ed}, {"ZLDFF1B 6(Z7.D), P4.Z, [Z13.D]", 0xc426f0ed}, {"ZLDFF1D 8(Z14.D), P6.Z, [Z6.D]", 0xc5a1f9c6}, {"ZLDFF1H 6(Z7.S), P4.Z, [Z13.S]", 0x84a3f0ed}, {"ZLDFF1H 6(Z7.D), P4.Z, [Z13.D]", 0xc4a3f0ed}, {"ZLDFF1SB 6(Z7.S), P4.Z, [Z13.S]", 0x8426b0ed}, {"ZLDFF1SB 6(Z7.D), P4.Z, [Z13.D]", 0xc426b0ed}, {"ZLDFF1SH 6(Z7.S), P4.Z, [Z13.S]", 0x84a3b0ed}, {"ZLDFF1SH 6(Z7.D), P4.Z, [Z13.D]", 0xc4a3b0ed}, {"ZLDFF1SW 12(Z0.D), P6.Z, [Z25.D]", 0xc523b819}, {"ZLDFF1W 12(Z0.S), P6.Z, [Z25.S]", 0x8523f819}, {"ZLDFF1W 12(Z0.D), P6.Z, [Z25.D]", 0xc523f819}, {"ZST1B [Z8.S], P3, 6(Z15.S)", 0xe466ade8}, {"ZST1B [Z8.D], P3, 6(Z15.D)", 0xe446ade8}, {"ZST1D [Z16.D], P2, (Z5.D)", 0xe5c0a8b0}, {"ZST1H [Z8.S], P3, 6(Z15.S)", 0xe4e3ade8}, {"ZST1H [Z8.D], P3, 6(Z15.D)", 0xe4c3ade8}, {"ZST1W [Z16.S], P2, (Z5.S)", 0xe560a8b0}, {"ZST1W [Z16.D], P2, (Z5.D)", 0xe540a8b0}, } for _, tt := range tests { words := assembleArm64Words(t, arm64ExtProbeHead+"\t"+tt.stmt+"\n\tRET\n") if len(words) != 2 { t.Fatalf("%s: got %d words, want the statement and RET", tt.stmt, len(words)) } if words[0] != tt.want { t.Errorf("%s:\n got %08x\n want %08x", tt.stmt, words[0], tt.want) } } } // TestArm64AssembleExtensionStage4Golden drives the SVE2.1 narrowing // two-to-one family through the full assembler: corpus text in, corpus word // out, with the two width extensions the arch-level golden test pins. func TestArm64AssembleExtensionStage4Golden(t *testing.T) { tests := []struct { stmt string want uint32 }{ {"ZADDHNB Z22.S, Z10.S, Z8.H", 0x45b66148}, {"ZADDHNT Z22.S, Z10.S, Z8.H", 0x45b66548}, {"ZRADDHNB Z22.S, Z10.S, Z8.H", 0x45b66948}, {"ZRADDHNT Z22.S, Z10.S, Z8.H", 0x45b66d48}, {"ZRSUBHNB Z22.S, Z10.S, Z8.H", 0x45b67948}, {"ZRSUBHNT Z22.S, Z10.S, Z8.H", 0x45b67d48}, {"ZSUBHNB Z22.S, Z10.S, Z8.H", 0x45b67148}, {"ZSUBHNT Z22.S, Z10.S, Z8.H", 0x45b67548}, {"ZADDHNB Z8.H, Z10.H, Z11.B", 0x4568614b}, {"ZSUBHNT Z26.D, Z4.D, Z13.S", 0x45fa748d}, // The pairwise and quadword-reduction classes: corpus lines plus a // width extension each. {"ZADDP Z15.B, Z0.B, P3.M, Z0.B", 0x4411ade0}, {"ZADDP Z9.S, Z2.S, P5.M, Z2.S", 0x4491b522}, {"ZADDPT Z23.D, Z13.D, P1.M, Z13.D", 0x04c406ed}, {"ZADDPT Z7.D, Z6.D, Z23.D", 0x04e708d7}, {"ZADDQV Z25.S, P3, V5.S4", 0x04852f25}, {"ZADDQV Z9.D, P1, V7.D2", 0x04c52527}, {"ZANDQV Z25.S, P3, V5.S4", 0x049e2f25}, {"ZEORQV Z25.S, P3, V5.S4", 0x049d2f25}, {"ZORQV Z25.S, P3, V5.S4", 0x049c2f25}, {"ZSMAXQV Z25.S, P3, V5.S4", 0x048c2f25}, {"ZSMINQV Z25.S, P3, V5.S4", 0x048e2f25}, {"ZUMAXQV Z25.S, P3, V5.S4", 0x048d2f25}, {"ZUMINQV Z25.S, P3, V5.S4", 0x048f2f25}, {"ZFADDQV Z25.S, P3, V5.S4", 0x6490af25}, {"ZFMAXNMQV Z25.S, P3, V5.S4", 0x6494af25}, {"ZFMAXQV Z25.S, P3, V5.S4", 0x6496af25}, {"ZFMINNMQV Z25.S, P3, V5.S4", 0x6495af25}, {"ZFMINQV Z25.S, P3, V5.S4", 0x6497af25}, // The shift-by-vector family. {"ZASR Z15.B, Z0.B, P3.M, Z0.B", 0x04108de0}, {"ZASR Z2.D, Z10.D, P3.M, Z10.D", 0x04d08c4a}, {"ZASR Z7.D, Z6.H, Z13.H", 0x046780cd}, {"ZASRR Z15.B, Z0.B, P3.M, Z0.B", 0x04148de0}, {"ZLSL Z2.D, Z10.D, P3.M, Z10.D", 0x04d38c4a}, {"ZLSL Z7.D, Z6.H, Z13.H", 0x04678ccd}, {"ZLSLR Z15.B, Z0.B, P3.M, Z0.B", 0x04178de0}, {"ZLSR Z15.B, Z0.B, P3.M, Z0.B", 0x04118de0}, {"ZLSR Z7.D, Z6.H, Z13.H", 0x046784cd}, {"ZLSRR Z15.B, Z0.B, P3.M, Z0.B", 0x04158de0}, } for _, tt := range tests { words := assembleArm64Words(t, arm64ExtProbeHead+"\t"+tt.stmt+"\n\tRET\n") if len(words) != 2 { t.Fatalf("%s: got %d words, want the statement and RET", tt.stmt, len(words)) } if words[0] != tt.want { t.Errorf("%s:\n got %08x\n want %08x", tt.stmt, words[0], tt.want) } if words[1] != 0xd65f03c0 { t.Errorf("%s: RET encoded %08x", tt.stmt, words[1]) } } } // TestArm64AssembleExtensionStage4Refusals pins the diagnostics the // narrowing, pairwise and quadword-reduction statements get from the layer: // the arrangement pair the single size field over-determines, the locked // pairwise top arrangement and the accumulator's bare predicate. func TestArm64AssembleExtensionStage4Refusals(t *testing.T) { tests := []struct { stmt string want string }{ {"ZADDHNB Z22.B, Z10.B, Z8.H", "want .H, .S or .D"}, {"ZADDHNB Z22.S, Z10.H, Z8.H", "must match"}, {"ZADDHNB Z22.S, Z10.S, Z8.S", "want .H"}, {"ZADDHNB Z22.D, Z10.D, Z8.H", "want .S"}, {"ZADDHNB Z22.Q, Z10.Q, Z8.D", "want .H, .S or .D"}, {"ZADDHNB Z22.S, Z10.S, Z8", "carries no arrangement suffix"}, {"ZADDP Z15.B, Z0.B, P3.Z, Z0.B", "merging qualifier"}, {"ZADDPT Z7.S, Z6.S, Z23.S", "want .D"}, {"ZADDQV Z25.S, P9, V5.S4", "outside P0-P7"}, {"ZADDQV Z25.S, P3/M, V5.S4", "takes no qualifier"}, {"ZADDQV Z25.S, P3, V5", "no quadword suffix"}, {"ZADDQV Z25.S, P3, V5.D2", "carries arrangement .D, want .S"}, } for _, tt := range tests { got := assembleArm64SrcError(t, arm64ExtProbeHead+"\t"+tt.stmt+"\n\tRET\n") if !strings.Contains(got, tt.want) { t.Errorf("%s: error %q does not name %q", tt.stmt, got, tt.want) } } } // TestArm64AssembleExtensionGatherRefusals pins the diagnostics the // gather/scatter statements get from the layer: the addressing-mode // mismatches, the locked list arrangements, the zeroing qualifier of the // loads against the bare predicates of the stores, and the immediate // scaling. func TestArm64AssembleExtensionGatherRefusals(t *testing.T) { tests := []struct { stmt string want string }{ {"ZLD1B (R6<<1)(R14), P4.Z, [Z13.B]", "scaled by <<0"}, {"ZLD1B (Z6.D)(R14), P4.Z, [Z13.B]", "wants two general registers"}, {"ZLD1B (Z6.S)(R14), P4.Z, [Z13.S]", "wants two general registers"}, {"ZLD1B (R6)(R14), P4/M, [Z13.B]", "wants the zeroing qualifier"}, {"ZLD1B (R6)(R14), P9.Z, [Z13.B]", "outside P0-P7"}, {"ZLD1B (R6)(ZR), P4.Z, [Z13.B]", "is not an extended-layer operand"}, {"ZLD1B (RSP)(R14), P4.Z, [Z13.B]", "is not an extended-layer operand"}, {"ZLD1D 12(Z14.D), P6.Z, [Z6.D]", "wants two general registers"}, {"ZLD1D 264(Z14.D), P6.Z, [Z6.D]", "wants two general registers"}, {"ZST1B [Z7.D], P4/Z, (R21)(R7)", "takes no qualifier"}, {"ZST1B [Z7.S], P4, (R21)(R7)", "carries arrangement .S, want .D"}, {"ZST1B [Z7.D], P4, (Z21.S)(R7)", "wants two general registers"}, } for _, tt := range tests { got := assembleArm64SrcError(t, arm64ExtProbeHead+"\t"+tt.stmt+"\n\tRET\n") if !strings.Contains(got, tt.want) { t.Errorf("%s: error %q does not name %q", tt.stmt, got, tt.want) } } }