Files
gasm-sdk/asm/arm64_ext_asm_test.go
T
2026-10-07 21:39:45 +02:00

1055 lines
43 KiB
Go

// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (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},
{"ZCNOT Z7.D, P4.M, Z13.D", 0x04dbb0ed},
{"ZCNOT Z7.D, P4.Z, Z13.D", 0x04cbb0ed},
{"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},
{"ZDECP P14.S, Z26.S", 0x25ad81da},
{"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},
// The three-source and bitwise combine families.
{"ZBCAX Z23.D, Z13.D, Z21.D, Z21.D", 0x046d3af5},
{"ZEOR3 Z23.D, Z13.D, Z21.D, Z21.D", 0x042d3af5},
{"ZBSL Z23.D, Z13.D, Z21.D, Z21.D", 0x042d3ef5},
{"ZBSL1N Z23.D, Z13.D, Z21.D, Z21.D", 0x046d3ef5},
{"ZBSL2N Z23.D, Z13.D, Z21.D, Z21.D", 0x04ad3ef5},
{"ZNBSL Z23.D, Z13.D, Z21.D, Z21.D", 0x04ed3ef5},
{"ZBDEP Z7.D, Z23.D, Z13.D", 0x45c7b6ed},
{"ZBEXT Z7.D, Z23.D, Z13.D", 0x45c7b2ed},
{"ZBGRP Z7.D, Z23.D, Z13.D", 0x45c7baed},
{"ZEORBT Z7.D, Z23.D, Z13.D", 0x45c792ed},
{"ZEORTB Z7.D, Z23.D, Z13.D", 0x45c796ed},
// The BFloat16 arithmetic core, halfwords throughout.
{"ZBF1CVT Z11.B, Z6.H", 0x65083966},
{"ZBF1CVTLT Z11.B, Z6.H", 0x65093966},
{"ZBF2CVT Z11.B, Z6.H", 0x65083d66},
{"ZBF2CVTLT Z11.B, Z6.H", 0x65093d66},
{"ZF1CVT Z11.B, Z6.H", 0x65083166},
{"ZF1CVTLT Z11.B, Z6.H", 0x65093166},
{"ZF2CVT Z11.B, Z6.H", 0x65083566},
{"ZF2CVTLT Z11.B, Z6.H", 0x65093566},
{"ZBFADD Z23.H, Z13.H, P1.M, Z13.H", 0x650086ed},
{"ZBFADD Z7.H, Z6.H, Z23.H", 0x650700d7},
{"ZBFCLAMP Z7.H, Z6.H, Z23.H", 0x642724d7},
{"ZBFMAX Z23.H, Z13.H, P1.M, Z13.H", 0x650686ed},
{"ZBFMAXNM Z23.H, Z13.H, P1.M, Z13.H", 0x650486ed},
{"ZBFMIN Z23.H, Z13.H, P1.M, Z13.H", 0x650786ed},
{"ZBFMINNM Z23.H, Z13.H, P1.M, Z13.H", 0x650586ed},
{"ZBFMUL Z23.H, Z13.H, P1.M, Z13.H", 0x650286ed},
{"ZBFMUL Z7.H, Z6.H, Z23.H", 0x650708d7},
{"ZBFSUB Z23.H, Z13.H, P1.M, Z13.H", 0x650186ed},
{"ZBFSUB Z7.H, Z6.H, Z23.H", 0x650704d7},
// The rest of the BFloat16 wall.
{"ZBFCVT Z13.S, P1.M, Z22.H", 0x658aa5b6},
{"ZBFCVT Z13.S, P1.Z, Z22.H", 0x649ac5b6},
{"ZBFCVTNT Z13.S, P1.M, Z22.H", 0x648aa5b6},
{"ZBFDOT Z7.H, Z6.H, Z23.S", 0x646780d7},
{"ZBFMLA Z23.H, Z13.H, P1.M, Z22.H", 0x653705b6},
{"ZBFMLALB Z7.H, Z6.H, Z23.S", 0x64e780d7},
{"ZBFMLALT Z7.H, Z6.H, Z23.S", 0x64e784d7},
{"ZBFMLS Z23.H, Z13.H, P1.M, Z22.H", 0x653725b6},
{"ZBFMLSLB Z7.H, Z6.H, Z23.S", 0x64e7a0d7},
{"ZBFMLSLT Z7.H, Z6.H, Z23.S", 0x64e7a4d7},
{"ZBFMMLA Z7.H, Z6.H, Z23.S", 0x6467e4d7},
{"ZBFSCALE Z23.H, Z13.H, P1.M, Z13.H", 0x650986ed},
// The multiple-structure loads and stores.
{"ZLD2B (R6)(R14), P4.Z, [Z13.B, Z14.B]", 0xa426d1cd},
{"ZLD2H (R6<<1)(R14), P4.Z, [Z13.H, Z14.H]", 0xa4a6d1cd},
{"ZLD2Q (R6<<4)(R14), P4.Z, [Z13.Q, Z14.Q]", 0xa4a691cd},
{"ZLD3W (R6<<2)(R14), P4.Z, [Z13.S, Z14.S, Z15.S]", 0xa546d1cd},
{"ZLD4B (R6)(R14), P4.Z, [Z13.B, Z14.B, Z15.B, Z16.B]", 0xa466d1cd},
{"ZLD4D (R6<<3)(R14), P4.Z, [Z13.D, Z14.D, Z15.D, Z16.D]", 0xa5e6d1cd},
{"ZST2B [Z8.B, Z9.B], P3, (R6)(RSP)", 0xe4266fe8},
{"ZST2Q [Z8.Q, Z9.Q], P3, (R6<<4)(RSP)", 0xe4660fe8},
{"ZST3D [Z8.D, Z9.D, Z10.D], P3, (R6<<3)(RSP)", 0xe5c66fe8},
{"ZST4Q [Z8.Q, Z9.Q, Z10.Q, Z11.Q], P3, (R6<<4)(RSP)", 0xe4e60fe8},
// The rest of the BFloat16 wall.
{"ZBFCVT Z13.S, P1.M, Z22.H", 0x658aa5b6},
{"ZBFCVT Z13.S, P1.Z, Z22.H", 0x649ac5b6},
{"ZBFCVTNT Z13.S, P1.M, Z22.H", 0x648aa5b6},
{"ZBFDOT Z7.H, Z6.H, Z23.S", 0x646780d7},
{"ZBFMLA Z23.H, Z13.H, P1.M, Z22.H", 0x653705b6},
{"ZBFMLALB Z7.H, Z6.H, Z23.S", 0x64e780d7},
{"ZBFMLALT Z7.H, Z6.H, Z23.S", 0x64e784d7},
{"ZBFMLS Z23.H, Z13.H, P1.M, Z22.H", 0x653725b6},
{"ZBFMLSLB Z7.H, Z6.H, Z23.S", 0x64e7a0d7},
{"ZBFMLSLT Z7.H, Z6.H, Z23.S", 0x64e7a4d7},
{"ZBFMMLA Z7.H, Z6.H, Z23.S", 0x6467e4d7},
{"ZBFSCALE Z23.H, Z13.H, P1.M, Z13.H", 0x650986ed},
// The last-active vector shape and the compares to a predicate.
{"ZCLASTA Z15.B, Z0.B, P3, Z0.B", 0x05288de0},
{"ZCLASTB Z15.B, Z0.B, P3, Z0.B", 0x05298de0},
{"ZCMPEQ Z0.H, Z2.H, P0.Z, P14.H", 0x2440a04e},
{"ZCMPEQ Z0.D, Z12.S, P0.Z, P14.S", 0x2480218e},
{"ZCMPHS Z0.H, Z2.H, P0.Z, P14.H", 0x2440004e},
{"ZCMPNE Z0.D, Z12.S, P0.Z, P14.S", 0x2480219e},
// The vector-length arithmetic.
{"ADDPL $9, R11, RSP", 0x046b513f},
{"ADDVL $9, R11, RSP", 0x042b513f},
{"RDVL $1, R27", 0x04bf503b},
}
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", "/M 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"},
// The multiple-structure lists.
{"ZLD3B (R6)(R14), P4.Z, [Z13.B, Z14.B]", "is not an extended-layer operand"},
{"ZLD2B (R6)(R14), P4.Z, [Z13.B, Z15.B]", "is not an extended-layer operand"},
{"ZLD2B (R6)(R14), P4.Z, [Z13.B, Z14.S]", "is not an extended-layer operand"},
}
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)
}
}
}
// TestArm64AssembleExtensionShiftImmGolden drives the shift-immediate
// classes through the full assembler: every corpus line of the three
// families, corpus text in, corpus word out. The narrowing and widening
// three-vector shifts and the predicated saturating left shifts carry the
// amount in the class's tsz:imm3 fields, whose tsz bits double as the
// arrangement spelling.
func TestArm64AssembleExtensionShiftImmGolden(t *testing.T) {
tests := []struct {
stmt string
want uint32
}{
{"ZSHRNB $9, Z7.S, Z23.H", 0x453710f7},
{"ZSHRNT $9, Z7.S, Z23.H", 0x453714f7},
{"ZRSHRNB $9, Z7.S, Z23.H", 0x453718f7},
{"ZRSHRNT $9, Z7.S, Z23.H", 0x45371cf7},
{"ZSQSHRNB $9, Z7.S, Z23.H", 0x453720f7},
{"ZSQSHRNT $9, Z7.S, Z23.H", 0x453724f7},
{"ZSQRSHRNB $9, Z7.S, Z23.H", 0x453728f7},
{"ZSQRSHRNT $9, Z7.S, Z23.H", 0x45372cf7},
{"ZSQSHRUNB $9, Z7.S, Z23.H", 0x453700f7},
{"ZSQSHRUNT $9, Z7.S, Z23.H", 0x453704f7},
{"ZUQSHRNB $9, Z7.S, Z23.H", 0x453730f7},
{"ZUQSHRNT $9, Z7.S, Z23.H", 0x453734f7},
{"ZUQRSHRNB $9, Z7.S, Z23.H", 0x453738f7},
{"ZUQRSHRNT $9, Z7.S, Z23.H", 0x45373cf7},
{"ZSSHLLB $5, Z22.S, Z10.D", 0x4545a2ca},
{"ZSSHLLT $5, Z22.S, Z10.D", 0x4545a6ca},
{"ZUSHLLB $5, Z22.S, Z10.D", 0x4545aaca},
{"ZUSHLLT $5, Z22.S, Z10.D", 0x4545aeca},
{"ZSQSHL $6, Z23.B, P1.M, Z23.B", 0x040685d7},
{"ZSQSHLU $6, Z23.B, P1.M, Z23.B", 0x040f85d7},
{"ZUQSHL $6, Z23.B, P1.M, Z23.B", 0x040785d7},
// Width extensions beyond the corpus lines.
{"ZSHRNB $8, Z6.H, Z22.B", 0x452810d6},
{"ZSQSHRNT $16, Z5.D, Z4.S", 0x457024a4},
{"ZSSHLLB $0, Z3.B, Z2.H", 0x4508a062},
{"ZSQSHL $20, Z8.S, P4.M, Z8.S", 0x04469288},
// The same-size right shifts, the accumulator and the extract.
{"ZSRI $6, Z7.D, Z23.D", 0x45daf0f7},
{"ZSSRA $6, Z7.D, Z23.D", 0x45dae0f7},
{"ZUSRA $6, Z7.D, Z23.D", 0x45dae4f7},
{"ZSRSRA $6, Z7.D, Z23.D", 0x45dae8f7},
{"ZURSRA $6, Z7.D, Z23.D", 0x45daecf7},
{"ZASR $6, Z7.D, Z23.D", 0x04fa90f7},
{"ZASR $6, Z23.B, P1.M, Z23.B", 0x04008557},
{"ZASRD $6, Z23.B, P1.M, Z23.B", 0x04048557},
{"ZXAR $6, Z23.B, Z21.B, Z21.B", 0x042a36f5},
}
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])
}
}
}
// TestArm64AssembleExtensionShiftImmRefusals pins the diagnostics the
// shift-immediate statements get from the layer: the element-step pairs
// the classes take, the amount ranges and the read-back and qualifier
// rules of the predicated class.
func TestArm64AssembleExtensionShiftImmRefusals(t *testing.T) {
tests := []struct {
stmt string
want string
}{
{"ZSHRNB $9, Z7.S, Z23.S", "one element step below"},
{"ZSHRNB $9, Z7.S, Z23.B", "one element step below"},
{"ZSHRNB $17, Z7.S, Z23.H", "outside the range 1..16"},
{"ZSHRNB $0, Z7.S, Z23.H", "outside the range 1..16"},
{"ZSHRNB $9, LSL #8, Z7.S, Z23.H", "takes no LSL shift"},
{"ZSSHLLB $32, Z22.S, Z10.D", "outside the range 0..31"},
{"ZSSHLLB $5, Z22.S, Z10.Q", "one element step above .S"},
{"ZSQSHL $6, Z23.B, P1.Z, Z23.B", "/M"},
{"ZSQSHL $6, Z23.B, P1.M, Z22.B", "the same register Zdn"},
{"ZSQSHL $6, Z23.Q, P1.M, Z23.Q", "no size encoding"},
{"ZSQSHL $8, Z23.B, P1.M, Z23.B", "outside the range 1..7"},
{"ZSRI $6, Z7.B, Z23.H", "must match"},
{"ZSRI $0, Z7.D, Z23.D", "outside the range 1..63"},
{"ZASR $8, Z23.B, P1.M, Z23.B", "outside the range 1..7"},
{"ZXAR $6, Z23.B, Z21.B, Z20.B", "the same register Zdn"},
{"ZXAR $6, Z23.B, Z21.H, Z21.H", "must match"},
}
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)
}
}
}