feat(asm): encode the arm64 SVE2 crypto, counter and reduction families
Assisted-by: GLM 5.3
This commit is contained in:
1 parent
dac0a5b51b
commit
6faf850793
5 files changed
+1211
-16
No files matched your search
+54
-13
@@ -15,7 +15,11 @@
|
||||
// classes, and for the predicate family Pm.B, Pn.B, Pg/Z (or Pg.Z), Pd.B
|
||||
// for the logical operations, Pn.B, Pg.Z, Pd.B for the breaks, Pm.T, Pn.T,
|
||||
// Pd.T for the permutations, Rm, Rn, Pd.T for the while compares, PN8-PN15
|
||||
// for the counter destinations, and the bare SETFFR.
|
||||
// for the counter destinations, and the bare SETFFR. Stage three adds the
|
||||
// crypto family (Zn.T, Zd.T, Zd.T read-back and the in-place Zd.T, Zd.T),
|
||||
// the predicate counters (Pn.T, Pg, Rd; Pn.T, ZR; Rd, Pn.T, Rd; ZR and R
|
||||
// terminators) and the reductions (Zn.T, Pg, Vd over the SIMD register
|
||||
// V0-V31, with ZR and RSP accepted where the classes take them).
|
||||
|
||||
package asm
|
||||
|
||||
@@ -45,7 +49,7 @@ func arm64ExtStatement(mnem string, ops []*ast.Operand) (extops []arch.ExtOperan
|
||||
if _, ok := LookupExtension(arch.ARM64, mnem); !ok {
|
||||
return nil, false, nil
|
||||
}
|
||||
if !arm64ExtPinned(ops) {
|
||||
if !arm64ExtPinned(mnem, ops) {
|
||||
return nil, false, nil
|
||||
}
|
||||
out := make([]arch.ExtOperand, 0, len(ops))
|
||||
@@ -87,6 +91,18 @@ func arm64ExtStatement(mnem string, ops []*ast.Operand) (extops []arch.ExtOperan
|
||||
out = append(out, ext)
|
||||
continue
|
||||
}
|
||||
if text == "ZR" {
|
||||
out = append(out, arch.ExtZeroRegister())
|
||||
continue
|
||||
}
|
||||
if text == "RSP" {
|
||||
out = append(out, arch.ExtStackPointer())
|
||||
continue
|
||||
}
|
||||
if ext, ok := arm64ExtSIMD(text); ok {
|
||||
out = append(out, ext)
|
||||
continue
|
||||
}
|
||||
if ext, ok := arm64ExtGeneral(text); ok {
|
||||
out = append(out, ext)
|
||||
continue
|
||||
@@ -96,18 +112,27 @@ func arm64ExtStatement(mnem string, ops []*ast.Operand) (extops []arch.ExtOperan
|
||||
return out, true, nil
|
||||
}
|
||||
|
||||
// arm64ExtPinned reports whether the statement belongs to the layer: any
|
||||
// operand is a scalable vector, predicate or predicate-as-counter register,
|
||||
// the shapes only the extension layer reads, or the statement carries no
|
||||
// operands at all and the mnemonic's zero-operand forms claim it. The test
|
||||
// is deliberately loose about the suffixes: P0/B is not a spelling the
|
||||
// layer takes, but the P of it makes the statement the layer's, and the
|
||||
// conversion then diagnoses the operand precisely instead of leaving it to
|
||||
// a scalar path that would report an unrelated register error.
|
||||
func arm64ExtPinned(ops []*ast.Operand) bool {
|
||||
// arm64ExtPinned reports whether the statement belongs to the layer. A
|
||||
// mnemonic the extension layer registers on its own, one the generated
|
||||
// arm64 table does not know, owns every one of its statements: no scalar
|
||||
// path could mean it instead, and the layer's diagnostics replace the
|
||||
// unsupported-instruction complaint. A mnemonic both tables carry (the
|
||||
// SVE aliases of ADD, SUB and MUL) keeps the operand-shape test: any
|
||||
// operand is a scalable vector, predicate or predicate-as-counter
|
||||
// register, the shapes only the extension layer reads, or the statement
|
||||
// carries no operands at all and the mnemonic's zero-operand forms claim
|
||||
// it. The shape test is deliberately loose about the suffixes: P0/B is
|
||||
// not a spelling the layer takes, but the P of it makes the statement the
|
||||
// layer's, and the conversion then diagnoses the operand precisely
|
||||
// instead of leaving it to a scalar path that would report an unrelated
|
||||
// register error.
|
||||
func arm64ExtPinned(mnem string, ops []*ast.Operand) bool {
|
||||
if len(ops) == 0 {
|
||||
return true
|
||||
}
|
||||
if _, shared := a64InstrTable[mnem]; !shared {
|
||||
return true
|
||||
}
|
||||
for _, op := range ops {
|
||||
if op.Kind == ast.OpImmediate {
|
||||
continue
|
||||
@@ -230,8 +255,9 @@ func arm64ExtCounter(text string) (arch.ExtOperand, bool) {
|
||||
}
|
||||
|
||||
// arm64ExtGeneral parses a general register operand: R0..R30, the plain
|
||||
// spelling the while-compare forms take. The register range is left to the
|
||||
// encoding, whose diagnostics name it.
|
||||
// spelling the while-compare forms take, beside the ZR and RSP spellings of
|
||||
// the thirty-first slot the conversion above reads. The register range is
|
||||
// left to the encoding, whose diagnostics name it.
|
||||
func arm64ExtGeneral(text string) (arch.ExtOperand, bool) {
|
||||
rest, ok := strings.CutPrefix(text, "R")
|
||||
if !ok {
|
||||
@@ -244,6 +270,21 @@ func arm64ExtGeneral(text string) (arch.ExtOperand, bool) {
|
||||
return arch.ExtOperand{Kind: arch.ExtGReg, Reg: reg}, true
|
||||
}
|
||||
|
||||
// arm64ExtSIMD parses a 128-bit SIMD register operand: V0..V31, written
|
||||
// bare, the scalar destination the reductions and the crypto read-back
|
||||
// forms take. The register range is left to the encoding.
|
||||
func arm64ExtSIMD(text string) (arch.ExtOperand, bool) {
|
||||
rest, ok := strings.CutPrefix(text, "V")
|
||||
if !ok {
|
||||
return arch.ExtOperand{}, false
|
||||
}
|
||||
reg, arr, ok := arm64ExtRegDigits(rest)
|
||||
if !ok || arr != arch.ExtArrNone {
|
||||
return arch.ExtOperand{}, false
|
||||
}
|
||||
return arch.ExtOperand{Kind: arch.ExtVReg, Reg: reg}, true
|
||||
}
|
||||
|
||||
// arm64ExtRegDigits parses the digits and optional arrangement suffix of a
|
||||
// register spelling once the letter prefix is gone.
|
||||
func arm64ExtRegDigits(text string) (reg int, arr arch.ExtArrangement, ok bool) {
|
||||
|
||||
@@ -424,3 +424,141 @@ func TestArm64AssembleExtensionStage2Golden(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+89
-2
@@ -273,12 +273,99 @@ func TestExtensionNamesARM64(t *testing.T) {
|
||||
"ZZIP2",
|
||||
"ZZIPQ1",
|
||||
"ZZIPQ2",
|
||||
// The stage-three families: the SVE2 crypto group, the predicate
|
||||
// counters and loop terminators with the 32-bit while compares, and
|
||||
// the reductions into a SIMD register.
|
||||
"ZADCLB",
|
||||
"ZADCLT",
|
||||
"ZSBCLB",
|
||||
"ZSBCLT",
|
||||
"ZRAX1",
|
||||
"ZSM4EKEY",
|
||||
"ZSM4E",
|
||||
"ZAESD",
|
||||
"ZAESE",
|
||||
"ZAESIMC",
|
||||
"ZAESMC",
|
||||
"CTERMEQ",
|
||||
"CTERMEQW",
|
||||
"CTERMNE",
|
||||
"CTERMNEW",
|
||||
"PCNTP",
|
||||
"PFIRSTP",
|
||||
"PLASTP",
|
||||
"PDECP",
|
||||
"PINCP",
|
||||
"PSQDECP",
|
||||
"PSQINCP",
|
||||
"PUQDECP",
|
||||
"PUQINCP",
|
||||
"PUQDECPW",
|
||||
"PUQINCPW",
|
||||
"PSQDECPW",
|
||||
"PSQINCPW",
|
||||
"PWHILEGEW",
|
||||
"PWHILEGTW",
|
||||
"PWHILEHIW",
|
||||
"PWHILEHSW",
|
||||
"PWHILELEW",
|
||||
"PWHILELOW",
|
||||
"PWHILELSW",
|
||||
"PWHILELTW",
|
||||
"ZSADDVD",
|
||||
"ZUADDVD",
|
||||
"ZANDVB",
|
||||
"ZANDVH",
|
||||
"ZANDVS",
|
||||
"ZANDVD",
|
||||
"ZEORVB",
|
||||
"ZEORVH",
|
||||
"ZEORVS",
|
||||
"ZEORVD",
|
||||
"ZORVB",
|
||||
"ZORVH",
|
||||
"ZORVS",
|
||||
"ZORVD",
|
||||
"ZSMAXVB",
|
||||
"ZSMAXVH",
|
||||
"ZSMAXVS",
|
||||
"ZSMAXVD",
|
||||
"ZSMINVB",
|
||||
"ZSMINVH",
|
||||
"ZSMINVS",
|
||||
"ZSMINVD",
|
||||
"ZUMAXVB",
|
||||
"ZUMAXVH",
|
||||
"ZUMAXVS",
|
||||
"ZUMAXVD",
|
||||
"ZUMINVB",
|
||||
"ZUMINVH",
|
||||
"ZUMINVS",
|
||||
"ZUMINVD",
|
||||
"ZFADDVH",
|
||||
"ZFADDVS",
|
||||
"ZFADDVD",
|
||||
"ZFMAXNMVH",
|
||||
"ZFMAXNMVS",
|
||||
"ZFMAXNMVD",
|
||||
"ZFMAXVH",
|
||||
"ZFMAXVS",
|
||||
"ZFMAXVD",
|
||||
"ZFMINNMVH",
|
||||
"ZFMINNMVS",
|
||||
"ZFMINNMVD",
|
||||
"ZFMINVH",
|
||||
"ZFMINVS",
|
||||
"ZFMINVD",
|
||||
"ZFADDAH",
|
||||
"ZFADDAS",
|
||||
"ZFADDAD",
|
||||
}
|
||||
got := ExtensionNames(arch.ARM64)
|
||||
if strings.Join(got, ",") != strings.Join(want, ",") {
|
||||
t.Errorf("ExtensionNames(ARM64) = %v, want %v", got, want)
|
||||
}
|
||||
if n := len(arch.Extensions(arch.ARM64)); n != 167 {
|
||||
t.Errorf("the family registers %d instructions, want 164", n)
|
||||
if n := len(arch.Extensions(arch.ARM64)); n != 251 {
|
||||
t.Errorf("the family registers %d instructions, want 251", n)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user