From 5de9e985f8ccd8ce80407af496b008f0c30a16ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Balv=C3=ADn?= Date: Tue, 6 Oct 2026 21:25:48 +0200 Subject: [PATCH] feat(asm): encode the riscv64 vector load and store families The vector memory section stopped at the three hand-written shapes the GOROOT kernels use: every other spelling the toolchain accepts, the width variants, the constant-stride and indexed accesses, the segment families, the fault-only-first loads, the whole-register moves and the bit-mask pair were names without an encoder. The mnemonic now parses into its own fields (direction, segment count, addressing mode, width, fault-only-first and whole-register markers) and one encoder lays the word down, with the optional V0 mask operand and the toolchain's operand shapes. VSETVL joins the configuration settings. The toolchain's whole vector memory section, six hundred and twenty-eight statements of masked and unmasked forms, is a differential test against the oracle, word for word. Assisted-by: GLM 5.3 Flash --- asm/riscv_assemble.go | 299 ++++++++++++++---- asm/riscv_vector_test.go | 660 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 905 insertions(+), 54 deletions(-) create mode 100644 asm/riscv_vector_test.go diff --git a/asm/riscv_assemble.go b/asm/riscv_assemble.go index ecfe8ff..9d9c59d 100644 --- a/asm/riscv_assemble.go +++ b/asm/riscv_assemble.go @@ -3560,7 +3560,28 @@ func rvcCB(funct3, rs1 uint32, off int32) uint16 { // the vector mnemonics. func encodeRISCVVector(mnem string, ops []*ast.Operand) ([]byte, bool, error) { reg := regFromOperand + // The general vector load and store families: unit, constant-stride and + // indexed, with and without segments, the fault-only-first loads and the + // whole-register moves. riscvIsVecLS parses the mnemonic. + if riscvIsVecLS(mnem) { + return encodeRISCVVecLS(mnem, ops) + } switch mnem { + case "VSETVL": + // INSTR rs2, rs1, rd: the register form of the configuration + // setting. The toolchain writes funct7 0x40 above the standard + // fields, its own disambiguator against the immediate forms. + if len(ops) != 3 { + return nil, true, fmt.Errorf("%s expects 3 operands, got %d", mnem, len(ops)) + } + rs2 := reg(ops[0]) + rs1 := reg(ops[1]) + rd := reg(ops[2]) + if rs1 < 0 || rs2 < 0 || rd < 0 { + return nil, true, fmt.Errorf("%s: invalid register", mnem) + } + return wordLE(riscvRType(riscvEnc{0x57, 0x7, 0x40}, rd, rs1, rs2)), true, nil + case "VSETVLI", "VSETIVLI": // INSTR avl, vsew, vlmul, vta, vma, rd. if len(ops) != 6 { @@ -3616,60 +3637,6 @@ func encodeRISCVVector(mnem string, ops []*ast.Operand) ([]byte, bool, error) { ivli := mnem == "VSETIVLI" || isImmOperand(ops[0]) return wordLE(riscvVSetEnc(ivli, avl, riscvVType(vsew, vlmul, vta, vma), rd)), true, nil - case "VLE8V": - // Unit-stride load: INSTR (base), vd. - if len(ops) != 2 { - return nil, true, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops)) - } - rs1, ok := riscvVecMem(ops[0]) - if !ok { - return nil, true, fmt.Errorf("%s: invalid memory operand", mnem) - } - vd := reg(ops[1]) - if vd < 0 { - return nil, true, fmt.Errorf("%s: invalid vector register", mnem) - } - return wordLE(riscvVLSType(0x07, 0, 0, 0, 0, rs1, vd)), true, nil - - case "VSE8V", "VSE32V": - // Unit-stride store: INSTR vs3, (base). - if len(ops) != 2 { - return nil, true, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops)) - } - vs3 := reg(ops[0]) - rs1, ok := riscvVecMem(ops[1]) - if !ok { - return nil, true, fmt.Errorf("%s: invalid memory operand", mnem) - } - if vs3 < 0 { - return nil, true, fmt.Errorf("%s: invalid vector register", mnem) - } - width := 0 - if mnem == "VSE32V" { - width = 6 - } - return wordLE(riscvVLSType(0x27, 0, 0, width, 0, rs1, vs3)), true, nil - - case "VLSSEG4E32V", "VLSSEG8E32V": - // Constant-stride segmented load: INSTR (base), stride, vd. - if len(ops) != 3 { - return nil, true, fmt.Errorf("%s expects 3 operands, got %d", mnem, len(ops)) - } - rs1, ok := riscvVecMem(ops[0]) - if !ok { - return nil, true, fmt.Errorf("%s: invalid memory operand", mnem) - } - rs2 := reg(ops[1]) - vd := reg(ops[2]) - if rs2 < 0 || vd < 0 { - return nil, true, fmt.Errorf("%s: invalid register operand", mnem) - } - nf := 3 // 4 fields - if mnem == "VLSSEG8E32V" { - nf = 7 // 8 fields - } - return wordLE(riscvVLSType(0x07, nf, 2, 6, int32(rs2), rs1, vd)), true, nil - case "VADDVV", "VXORVV", "VMSNEVV": // Vector-vector: INSTR vs1, vs2, vd. if len(ops) != 3 { @@ -3781,6 +3748,230 @@ func riscvVecMem(op *ast.Operand) (rs1 int, ok bool) { return rs1, rs1 >= 0 } +// riscvVecLS is one parsed vector load/store mnemonic: the direction, the +// field counts and the fixed rs2 content (0 for plain forms, the +// fault-only-first marker, the mask pair's 11 or the whole-register marker). +type riscvVecLS struct { + load bool // true for the VL families, false for the VS families + nf int // segment count minus one + mop int // 0 unit, 1 indexed-ux, 2 constant-stride, 3 indexed-ox + width int // 0 = 8-bit, 5 = 16-bit, 6 = 32-bit, 7 = 64-bit + ff bool // fault-only-first: the fixed rs2 field carries 16 + rs2f int // fixed rs2 field: the whole-register and mask markers +} + +// riscvVecWidths maps the width segment of a vector load/store name onto the +// instruction's width field. +var riscvVecWidths = map[string]int{"8": 0, "16": 5, "32": 6, "64": 7} + +// riscvParseVecLS parses a vector load/store mnemonic into its fields. The +// families the toolchain spells: the unit, constant-stride and indexed +// accesses (VLE8V, VLSE8V, VLUXEI8V, VLOXEI8V and the stores), each with its +// segment variants (VLSEG2E8V, VLSSEG2E8V, VLUXSEG2EI8V, ...), the +// fault-only-first loads (VLE8FFV, VLSEG2E8FFV), the whole-register moves +// (VL1RV, VL2RE64V, VS8RV) and the bit-mask pair (VLMV, VSMV). +func riscvParseVecLS(m string) (riscvVecLS, bool) { + // The whole-register spellings and the mask pair: exact names. + whole := func(load bool, nf, rs2f int) (riscvVecLS, bool) { + return riscvVecLS{load: load, nf: nf, rs2f: rs2f}, true + } + switch m { + case "VLMV": + return whole(true, 0, 11) + case "VSMV": + return whole(false, 0, 11) + case "VL1RV": + return whole(true, 0, 8) + case "VS1RV": + return whole(false, 0, 8) + case "VL2RV": + return whole(true, 1, 8) + case "VS2RV": + return whole(false, 1, 8) + case "VL4RV": + return whole(true, 3, 8) + case "VS4RV": + return whole(false, 3, 8) + case "VL8RV": + return whole(true, 7, 8) + case "VS8RV": + return whole(false, 7, 8) + } + // VL{n}RE{w}V: the whole-register loads with an explicit width; the + // encoding is the width-less spelling's with the width field filled. + if len(m) >= 7 && m[1] == 'L' && m[2] >= '1' && m[2] <= '8' && m[3:5] == "RE" && strings.HasSuffix(m, "V") { + n := int(m[2] - '0') + w, ok := riscvParseVecLSWidth(m[5 : len(m)-1]) + if !ok { + return riscvVecLS{}, false + } + rs2f := 8 + return riscvVecLS{load: true, nf: n - 1, width: w, rs2f: rs2f}, true + } + if len(m) < 4 || m[0] != 'V' || (m[1] != 'L' && m[1] != 'S') { + return riscvVecLS{}, false + } + v := riscvVecLS{load: m[1] == 'L'} + rest := m[2:] + // The segment families carry the count: SEGE, SSEGE, UXSEGEI, + // OXSEGEI. + for _, fam := range []struct { + prefix string + mop int + ei bool + }{ + {"SSEG", 2, false}, + {"UXSEG", 1, true}, + {"OXSEG", 3, true}, + {"SEG", 0, false}, + } { + if !strings.HasPrefix(rest, fam.prefix) { + continue + } + tail := rest[len(fam.prefix):] + if len(tail) < 3 || tail[0] < '2' || tail[0] > '8' || tail[1] != 'E' { + return riscvVecLS{}, false + } + v.nf = int(tail[0] - '0') + v.nf-- // the field is the count minus one + tail = tail[2:] + if fam.ei { + if !strings.HasPrefix(tail, "I") { + return riscvVecLS{}, false + } + tail = tail[1:] + } + v.mop = fam.mop + rest = tail + break + } + if v.nf == 0 { + // The flat families: SEV, UXEIV, OXEIV, EV. + switch { + case strings.HasPrefix(rest, "SE"): + v.mop = 2 + rest = rest[2:] + case strings.HasPrefix(rest, "UXEI"): + v.mop = 1 + rest = rest[4:] + case strings.HasPrefix(rest, "OXEI"): + v.mop = 3 + rest = rest[4:] + case strings.HasPrefix(rest, "E"): + rest = rest[1:] + default: + return riscvVecLS{}, false + } + } + // The tail: V, or FFV on the fault-only-first loads. + ff := false + if strings.HasSuffix(rest, "FFV") { + ff = v.load + rest = rest[:len(rest)-3] + } else if strings.HasSuffix(rest, "V") { + rest = rest[:len(rest)-1] + } else { + return riscvVecLS{}, false + } + w, ok := riscvParseVecLSWidth(rest) + if !ok { + return riscvVecLS{}, false + } + v.width = w + v.ff = ff + if ff { + v.rs2f = 16 + } + return v, true +} + +// riscvParseVecLSWidth parses a vector width segment ("8", "16", "32", "64") +// onto its width field. The second result reports whether the text is a +// width the families carry. +func riscvParseVecLSWidth(s string) (int, bool) { + w, ok := riscvVecWidths[s] + return w, ok +} + +// riscvIsVecLS reports whether m is one of the vector load/store mnemonics +// encodeRISCVVecLS handles. +func riscvIsVecLS(m string) bool { + _, ok := riscvParseVecLS(m) + return ok +} + +// encodeRISCVVecLS encodes one vector load or store. The operand shapes are +// the toolchain's: (base), vd for the unit loads; (base), rs2|vs2 [, V0], vd +// for the stride, indexed and segment forms with their optional V0 mask; +// stores mirror them with vs3 first and (base) last. +func encodeRISCVVecLS(mnem string, ops []*ast.Operand) ([]byte, bool, error) { + v, ok := riscvParseVecLS(mnem) + if !ok { + return nil, true, fmt.Errorf("unsupported vector load/store %q", mnem) + } + op := uint32(0x27) + if v.load { + op = 0x07 + } + strided := v.mop == 2 + indexed := v.mop == 1 || v.mop == 3 + whole := v.rs2f == 2 || v.rs2f == 8 + + // Split the operands: the memory end fixes one operand, the register end + // the other, and a V0 beside the register end is the mask. + memIdx, regIdx := 0, len(ops)-1 + if !v.load { + memIdx, regIdx = len(ops)-1, 0 + } + rs1, ok := riscvVecMem(ops[memIdx]) + if !ok { + return nil, true, fmt.Errorf("%s: invalid memory operand", mnem) + } + vd := regFromOperand(ops[regIdx]) + if vd < 0 { + kind := "vd" + if !v.load { + kind = "vs1" + } + return nil, true, fmt.Errorf("%s: expected vector register in %s position", mnem, kind) + } + rs2 := v.rs2f + masked := false + for _, mid := range ops[min(memIdx, regIdx)+1 : max(memIdx, regIdx)] { + // The mask operand is the vector register V0: name-checked, so an + // integer X0 in the stride position is not mistaken for it. + if regFromOperand(mid) == 0 && strings.HasPrefix(strings.ToUpper(mid.Raw), "V") { + masked = true + continue + } + if !strided && !indexed { + return nil, true, fmt.Errorf("%s: too many operands for instruction", mnem) + } + if rs2 != v.rs2f { + return nil, true, fmt.Errorf("%s: too many operands for instruction", mnem) + } + rs2 = regFromOperand(mid) + if rs2 < 0 { + return nil, true, fmt.Errorf("%s: invalid register operand", mnem) + } + if indexed && strings.HasPrefix(strings.ToUpper(mid.Raw), "X") { + return nil, true, fmt.Errorf("%s: expected vector register in vs2 position", mnem) + } + if strided && !strings.HasPrefix(strings.ToUpper(mid.Raw), "X") { + return nil, true, fmt.Errorf("%s: expected integer register in rs2 position", mnem) + } + } + if masked && whole { + return nil, true, fmt.Errorf("%s: too many operands for instruction", mnem) + } + word := uint32(v.nf&7)<<29 | uint32(v.mop&3)<<26 | uint32(rs2&0x1F)<<20 | + uint32(rs1&0x1F)<<15 | uint32(v.width&7)<<12 | uint32(vd&0x1F)<<7 | op + if !masked { + word |= 1 << 25 + } + return wordLE(word), true, nil +} + // Instruction type classifiers. func isRTypeInstr(m string) bool { switch m { diff --git a/asm/riscv_vector_test.go b/asm/riscv_vector_test.go new file mode 100644 index 0000000..8b8ce9a --- /dev/null +++ b/asm/riscv_vector_test.go @@ -0,0 +1,660 @@ +// Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) +// SPDX-License-Identifier: BSD-3-Clause + +package asm + +import ( + "os" + "path/filepath" + "testing" +) + +// TestRISCVVectorLS_Differential proves the vector load/store families and +// the configuration settings against the toolchain: the "V" extension's +// memory section of the toolchain's own testdata (unit, constant-stride and +// indexed accesses with their segments, the fault-only-first loads, the +// whole-register moves and the mask pair, every masked and unmasked form) +// assembled by gasm and by go tool asm must agree word for word. +func TestRISCVVectorLS_Differential(t *testing.T) { + src := `#include "textflag.h" + +TEXT ·vecls(SB), NOSPLIT, $0 + + VSETVLI X10, E8, M1, TU, MU, X12 + VSETVLI X10, E16, M1, TU, MU, X12 + VSETVLI X10, E32, M1, TU, MU, X12 + VSETVLI X10, E64, M1, TU, MU, X12 + VSETVLI X10, E32, M1, TU, MA, X12 + VSETVLI X10, E32, M1, TA, MA, X12 + VSETVLI X10, E32, M2, TA, MA, X12 + VSETVLI X10, E32, M4, TA, MA, X12 + VSETVLI X10, E32, M8, TA, MA, X12 + VSETVLI X10, E32, MF8, TA, MA, X12 + VSETVLI X10, E32, MF4, TA, MA, X12 + VSETVLI X10, E32, MF2, TA, MA, X12 + VSETVLI X10, E32, M1, TA, MA, X12 + VSETVLI $15, E32, M1, TA, MA, X12 + VSETIVLI $0, E32, M1, TA, MA, X12 + VSETIVLI $15, E32, M1, TA, MA, X12 + VSETIVLI $31, E32, M1, TA, MA, X12 + VSETVL X10, X11, X12 + + VLE8V (X10), V3 + VLE8V (X10), V0, V3 + VLE16V (X10), V3 + VLE16V (X10), V0, V3 + VLE32V (X10), V3 + VLE32V (X10), V0, V3 + VLE64V (X10), V3 + VLE64V (X10), V0, V3 + VSE8V V3, (X10) + VSE8V V3, V0, (X10) + VSE16V V3, (X10) + VSE16V V3, V0, (X10) + VSE32V V3, (X10) + VSE32V V3, V0, (X10) + VSE64V V3, (X10) + VSE64V V3, V0, (X10) + VLMV (X10), V3 + VSMV V3, (X10) + + VLSE8V (X10), X11, V3 + VLSE8V (X10), X11, V0, V3 + VLSE16V (X10), X11, V3 + VLSE16V (X10), X11, V0, V3 + VLSE32V (X10), X11, V3 + VLSE32V (X10), X11, V0, V3 + VLSE64V (X10), X11, V3 + VLSE64V (X10), X11, V0, V3 + VSSE8V V3, X11, (X10) + VSSE8V V3, X11, V0, (X10) + VSSE16V V3, X11, (X10) + VSSE16V V3, X11, V0, (X10) + VSSE32V V3, X11, (X10) + VSSE32V V3, X11, V0, (X10) + VSSE64V V3, X11, (X10) + VSSE64V V3, X11, V0, (X10) + + VLUXEI8V (X10), V2, V3 + VLUXEI8V (X10), V2, V0, V3 + VLUXEI16V (X10), V2, V3 + VLUXEI16V (X10), V2, V0, V3 + VLUXEI32V (X10), V2, V3 + VLUXEI32V (X10), V2, V0, V3 + VLUXEI64V (X10), V2, V3 + VLUXEI64V (X10), V2, V0, V3 + VLOXEI8V (X10), V2, V3 + VLOXEI8V (X10), V2, V0, V3 + VLOXEI16V (X10), V2, V3 + VLOXEI16V (X10), V2, V0, V3 + VLOXEI32V (X10), V2, V3 + VLOXEI32V (X10), V2, V0, V3 + VLOXEI64V (X10), V2, V3 + VLOXEI64V (X10), V2, V0, V3 + VSUXEI8V V3, V2, (X10) + VSUXEI8V V3, V2, V0, (X10) + VSUXEI16V V3, V2, (X10) + VSUXEI16V V3, V2, V0, (X10) + VSUXEI32V V3, V2, (X10) + VSUXEI32V V3, V2, V0, (X10) + VSUXEI64V V3, V2, (X10) + VSUXEI64V V3, V2, V0, (X10) + VSOXEI8V V3, V2, (X10) + VSOXEI8V V3, V2, V0, (X10) + VSOXEI16V V3, V2, (X10) + VSOXEI16V V3, V2, V0, (X10) + VSOXEI32V V3, V2, (X10) + VSOXEI32V V3, V2, V0, (X10) + VSOXEI64V V3, V2, (X10) + VSOXEI64V V3, V2, V0, (X10) + + VLE8FFV (X10), V8 + VLE16FFV (X10), V8 + VLE32FFV (X10), V8 + VLE64FFV (X10), V8 + VLE8FFV (X10), V0, V8 + VLE16FFV (X10), V0, V8 + VLE32FFV (X10), V0, V8 + VLE64FFV (X10), V0, V8 + + VLSEG2E8V (X10), V8 + VLSEG2E16V (X10), V8 + VLSEG2E32V (X10), V8 + VLSEG2E64V (X10), V8 + VLSEG2E8V (X10), V0, V8 + VLSEG2E16V (X10), V0, V8 + VLSEG2E32V (X10), V0, V8 + VLSEG2E64V (X10), V0, V8 + VLSEG3E8V (X10), V8 + VLSEG3E16V (X10), V8 + VLSEG3E32V (X10), V8 + VLSEG3E64V (X10), V8 + VLSEG3E8V (X10), V0, V8 + VLSEG3E16V (X10), V0, V8 + VLSEG3E32V (X10), V0, V8 + VLSEG3E64V (X10), V0, V8 + VLSEG4E8V (X10), V8 + VLSEG4E16V (X10), V8 + VLSEG4E32V (X10), V8 + VLSEG4E64V (X10), V8 + VLSEG4E8V (X10), V0, V8 + VLSEG4E16V (X10), V0, V8 + VLSEG4E32V (X10), V0, V8 + VLSEG4E64V (X10), V0, V8 + VLSEG5E8V (X10), V8 + VLSEG5E16V (X10), V8 + VLSEG5E32V (X10), V8 + VLSEG5E64V (X10), V8 + VLSEG5E8V (X10), V0, V8 + VLSEG5E16V (X10), V0, V8 + VLSEG5E32V (X10), V0, V8 + VLSEG5E64V (X10), V0, V8 + VLSEG6E8V (X10), V8 + VLSEG6E16V (X10), V8 + VLSEG6E32V (X10), V8 + VLSEG6E64V (X10), V8 + VLSEG6E8V (X10), V0, V8 + VLSEG6E16V (X10), V0, V8 + VLSEG6E32V (X10), V0, V8 + VLSEG6E64V (X10), V0, V8 + VLSEG7E8V (X10), V8 + VLSEG7E16V (X10), V8 + VLSEG7E32V (X10), V8 + VLSEG7E64V (X10), V8 + VLSEG7E8V (X10), V0, V8 + VLSEG7E16V (X10), V0, V8 + VLSEG7E32V (X10), V0, V8 + VLSEG7E64V (X10), V0, V8 + VLSEG8E8V (X10), V8 + VLSEG8E16V (X10), V8 + VLSEG8E32V (X10), V8 + VLSEG8E64V (X10), V8 + VLSEG8E8V (X10), V0, V8 + VLSEG8E16V (X10), V0, V8 + VLSEG8E32V (X10), V0, V8 + VLSEG8E64V (X10), V0, V8 + VSSEG2E8V V24, (X10) + VSSEG2E16V V24, (X10) + VSSEG2E32V V24, (X10) + VSSEG2E64V V24, (X10) + VSSEG2E8V V24, V0, (X10) + VSSEG2E16V V24, V0, (X10) + VSSEG2E32V V24, V0, (X10) + VSSEG2E64V V24, V0, (X10) + VSSEG3E8V V24, (X10) + VSSEG3E16V V24, (X10) + VSSEG3E32V V24, (X10) + VSSEG3E64V V24, (X10) + VSSEG3E8V V24, V0, (X10) + VSSEG3E16V V24, V0, (X10) + VSSEG3E32V V24, V0, (X10) + VSSEG3E64V V24, V0, (X10) + VSSEG4E8V V24, (X10) + VSSEG4E16V V24, (X10) + VSSEG4E32V V24, (X10) + VSSEG4E64V V24, (X10) + VSSEG4E8V V24, V0, (X10) + VSSEG4E16V V24, V0, (X10) + VSSEG4E32V V24, V0, (X10) + VSSEG4E64V V24, V0, (X10) + VSSEG5E8V V24, (X10) + VSSEG5E16V V24, (X10) + VSSEG5E32V V24, (X10) + VSSEG5E64V V24, (X10) + VSSEG5E8V V24, V0, (X10) + VSSEG5E16V V24, V0, (X10) + VSSEG5E32V V24, V0, (X10) + VSSEG5E64V V24, V0, (X10) + VSSEG6E8V V24, (X10) + VSSEG6E16V V24, (X10) + VSSEG6E32V V24, (X10) + VSSEG6E64V V24, (X10) + VSSEG6E8V V24, V0, (X10) + VSSEG6E16V V24, V0, (X10) + VSSEG6E32V V24, V0, (X10) + VSSEG6E64V V24, V0, (X10) + VSSEG7E8V V24, (X10) + VSSEG7E16V V24, (X10) + VSSEG7E32V V24, (X10) + VSSEG7E64V V24, (X10) + VSSEG7E8V V24, V0, (X10) + VSSEG7E16V V24, V0, (X10) + VSSEG7E32V V24, V0, (X10) + VSSEG7E64V V24, V0, (X10) + VSSEG8E8V V24, (X10) + VSSEG8E16V V24, (X10) + VSSEG8E32V V24, (X10) + VSSEG8E64V V24, (X10) + VSSEG8E8V V24, V0, (X10) + VSSEG8E16V V24, V0, (X10) + VSSEG8E32V V24, V0, (X10) + VSSEG8E64V V24, V0, (X10) + VLSEG2E8FFV (X10), V8 + VLSEG2E16FFV (X10), V8 + VLSEG2E32FFV (X10), V8 + VLSEG2E64FFV (X10), V8 + VLSEG2E8FFV (X10), V0, V8 + VLSEG2E16FFV (X10), V0, V8 + VLSEG2E32FFV (X10), V0, V8 + VLSEG2E64FFV (X10), V0, V8 + VLSEG3E8FFV (X10), V8 + VLSEG3E16FFV (X10), V8 + VLSEG3E32FFV (X10), V8 + VLSEG3E64FFV (X10), V8 + VLSEG3E8FFV (X10), V0, V8 + VLSEG3E16FFV (X10), V0, V8 + VLSEG3E32FFV (X10), V0, V8 + VLSEG3E64FFV (X10), V0, V8 + VLSEG4E8FFV (X10), V8 + VLSEG4E16FFV (X10), V8 + VLSEG4E32FFV (X10), V8 + VLSEG4E64FFV (X10), V8 + VLSEG4E8FFV (X10), V0, V8 + VLSEG4E16FFV (X10), V0, V8 + VLSEG4E32FFV (X10), V0, V8 + VLSEG4E64FFV (X10), V0, V8 + VLSEG5E8FFV (X10), V8 + VLSEG5E16FFV (X10), V8 + VLSEG5E32FFV (X10), V8 + VLSEG5E64FFV (X10), V8 + VLSEG5E8FFV (X10), V0, V8 + VLSEG5E16FFV (X10), V0, V8 + VLSEG5E32FFV (X10), V0, V8 + VLSEG5E64FFV (X10), V0, V8 + VLSEG6E8FFV (X10), V8 + VLSEG6E16FFV (X10), V8 + VLSEG6E32FFV (X10), V8 + VLSEG6E64FFV (X10), V8 + VLSEG6E8FFV (X10), V0, V8 + VLSEG6E16FFV (X10), V0, V8 + VLSEG6E32FFV (X10), V0, V8 + VLSEG6E64FFV (X10), V0, V8 + VLSEG7E8FFV (X10), V8 + VLSEG7E16FFV (X10), V8 + VLSEG7E32FFV (X10), V8 + VLSEG7E64FFV (X10), V8 + VLSEG7E8FFV (X10), V0, V8 + VLSEG7E16FFV (X10), V0, V8 + VLSEG7E32FFV (X10), V0, V8 + VLSEG7E64FFV (X10), V0, V8 + VLSEG8E8FFV (X10), V8 + VLSEG8E16FFV (X10), V8 + VLSEG8E32FFV (X10), V8 + VLSEG8E64FFV (X10), V8 + VLSEG8E8FFV (X10), V0, V8 + VLSEG8E16FFV (X10), V0, V8 + VLSEG8E32FFV (X10), V0, V8 + VLSEG8E64FFV (X10), V0, V8 + + VLSSEG2E8V (X10), X11, V8 + VLSSEG2E16V (X10), X11, V8 + VLSSEG2E32V (X10), X11, V8 + VLSSEG2E64V (X10), X11, V8 + VLSSEG2E8V (X10), X11, V0, V8 + VLSSEG2E16V (X10), X11, V0, V8 + VLSSEG2E32V (X10), X11, V0, V8 + VLSSEG2E64V (X10), X11, V0, V8 + VLSSEG3E8V (X10), X11, V8 + VLSSEG3E16V (X10), X11, V8 + VLSSEG3E32V (X10), X11, V8 + VLSSEG3E64V (X10), X11, V8 + VLSSEG3E8V (X10), X11, V0, V8 + VLSSEG3E16V (X10), X11, V0, V8 + VLSSEG3E32V (X10), X11, V0, V8 + VLSSEG3E64V (X10), X11, V0, V8 + VLSSEG4E8V (X10), X11, V8 + VLSSEG4E16V (X10), X11, V8 + VLSSEG4E32V (X10), X11, V8 + VLSSEG4E64V (X10), X11, V8 + VLSSEG4E8V (X10), X11, V0, V8 + VLSSEG4E16V (X10), X11, V0, V8 + VLSSEG4E32V (X10), X11, V0, V8 + VLSSEG4E64V (X10), X11, V0, V8 + VLSSEG5E8V (X10), X11, V8 + VLSSEG5E16V (X10), X11, V8 + VLSSEG5E32V (X10), X11, V8 + VLSSEG5E64V (X10), X11, V8 + VLSSEG5E8V (X10), X11, V0, V8 + VLSSEG5E16V (X10), X11, V0, V8 + VLSSEG5E32V (X10), X11, V0, V8 + VLSSEG5E64V (X10), X11, V0, V8 + VLSSEG6E8V (X10), X11, V8 + VLSSEG6E16V (X10), X11, V8 + VLSSEG6E32V (X10), X11, V8 + VLSSEG6E64V (X10), X11, V8 + VLSSEG6E8V (X10), X11, V0, V8 + VLSSEG6E16V (X10), X11, V0, V8 + VLSSEG6E32V (X10), X11, V0, V8 + VLSSEG6E64V (X10), X11, V0, V8 + VLSSEG7E8V (X10), X11, V8 + VLSSEG7E16V (X10), X11, V8 + VLSSEG7E32V (X10), X11, V8 + VLSSEG7E64V (X10), X11, V8 + VLSSEG7E8V (X10), X11, V0, V8 + VLSSEG7E16V (X10), X11, V0, V8 + VLSSEG7E32V (X10), X11, V0, V8 + VLSSEG7E64V (X10), X11, V0, V8 + VLSSEG8E8V (X10), X11, V8 + VLSSEG8E16V (X10), X11, V8 + VLSSEG8E32V (X10), X11, V8 + VLSSEG8E64V (X10), X11, V8 + VLSSEG8E8V (X10), X11, V0, V8 + VLSSEG8E16V (X10), X11, V0, V8 + VLSSEG8E32V (X10), X11, V0, V8 + VLSSEG8E64V (X10), X11, V0, V8 + VSSSEG2E8V V24, X11, (X10) + VSSSEG2E16V V24, X11, (X10) + VSSSEG2E32V V24, X11, (X10) + VSSSEG2E64V V24, X11, (X10) + VSSSEG2E8V V24, X11, V0, (X10) + VSSSEG2E16V V24, X11, V0, (X10) + VSSSEG2E32V V24, X11, V0, (X10) + VSSSEG2E64V V24, X11, V0, (X10) + VSSSEG3E8V V24, X11, (X10) + VSSSEG3E16V V24, X11, (X10) + VSSSEG3E32V V24, X11, (X10) + VSSSEG3E64V V24, X11, (X10) + VSSSEG3E8V V24, X11, V0, (X10) + VSSSEG3E16V V24, X11, V0, (X10) + VSSSEG3E32V V24, X11, V0, (X10) + VSSSEG3E64V V24, X11, V0, (X10) + VSSSEG4E8V V24, X11, (X10) + VSSSEG4E16V V24, X11, (X10) + VSSSEG4E32V V24, X11, (X10) + VSSSEG4E64V V24, X11, (X10) + VSSSEG4E8V V24, X11, V0, (X10) + VSSSEG4E16V V24, X11, V0, (X10) + VSSSEG4E32V V24, X11, V0, (X10) + VSSSEG4E64V V24, X11, V0, (X10) + VSSSEG5E8V V24, X11, (X10) + VSSSEG5E16V V24, X11, (X10) + VSSSEG5E32V V24, X11, (X10) + VSSSEG5E64V V24, X11, (X10) + VSSSEG5E8V V24, X11, V0, (X10) + VSSSEG5E16V V24, X11, V0, (X10) + VSSSEG5E32V V24, X11, V0, (X10) + VSSSEG5E64V V24, X11, V0, (X10) + VSSSEG6E8V V24, X11, (X10) + VSSSEG6E16V V24, X11, (X10) + VSSSEG6E32V V24, X11, (X10) + VSSSEG6E64V V24, X11, (X10) + VSSSEG6E8V V24, X11, V0, (X10) + VSSSEG6E16V V24, X11, V0, (X10) + VSSSEG6E32V V24, X11, V0, (X10) + VSSSEG6E64V V24, X11, V0, (X10) + VSSSEG7E8V V24, X11, (X10) + VSSSEG7E16V V24, X11, (X10) + VSSSEG7E32V V24, X11, (X10) + VSSSEG7E64V V24, X11, (X10) + VSSSEG7E8V V24, X11, V0, (X10) + VSSSEG7E16V V24, X11, V0, (X10) + VSSSEG7E32V V24, X11, V0, (X10) + VSSSEG7E64V V24, X11, V0, (X10) + VSSSEG8E8V V24, X11, (X10) + VSSSEG8E16V V24, X11, (X10) + VSSSEG8E32V V24, X11, (X10) + VSSSEG8E64V V24, X11, (X10) + VSSSEG8E8V V24, X11, V0, (X10) + VSSSEG8E16V V24, X11, V0, (X10) + VSSSEG8E32V V24, X11, V0, (X10) + VSSSEG8E64V V24, X11, V0, (X10) + + VLUXSEG2EI8V (X10), V4, V8 + VLUXSEG2EI16V (X10), V4, V8 + VLUXSEG2EI32V (X10), V4, V8 + VLUXSEG2EI64V (X10), V4, V8 + VLUXSEG2EI8V (X10), V4, V0, V8 + VLUXSEG2EI16V (X10), V4, V0, V8 + VLUXSEG2EI32V (X10), V4, V0, V8 + VLUXSEG2EI64V (X10), V4, V0, V8 + VLUXSEG3EI8V (X10), V4, V8 + VLUXSEG3EI16V (X10), V4, V8 + VLUXSEG3EI32V (X10), V4, V8 + VLUXSEG3EI64V (X10), V4, V8 + VLUXSEG3EI8V (X10), V4, V0, V8 + VLUXSEG3EI16V (X10), V4, V0, V8 + VLUXSEG3EI32V (X10), V4, V0, V8 + VLUXSEG3EI64V (X10), V4, V0, V8 + VLUXSEG4EI8V (X10), V4, V8 + VLUXSEG4EI16V (X10), V4, V8 + VLUXSEG4EI32V (X10), V4, V8 + VLUXSEG4EI64V (X10), V4, V8 + VLUXSEG4EI8V (X10), V4, V0, V8 + VLUXSEG4EI16V (X10), V4, V0, V8 + VLUXSEG4EI32V (X10), V4, V0, V8 + VLUXSEG4EI64V (X10), V4, V0, V8 + VLUXSEG5EI8V (X10), V4, V8 + VLUXSEG5EI16V (X10), V4, V8 + VLUXSEG5EI32V (X10), V4, V8 + VLUXSEG5EI64V (X10), V4, V8 + VLUXSEG5EI8V (X10), V4, V0, V8 + VLUXSEG5EI16V (X10), V4, V0, V8 + VLUXSEG5EI32V (X10), V4, V0, V8 + VLUXSEG5EI64V (X10), V4, V0, V8 + VLUXSEG6EI8V (X10), V4, V8 + VLUXSEG6EI16V (X10), V4, V8 + VLUXSEG6EI32V (X10), V4, V8 + VLUXSEG6EI64V (X10), V4, V8 + VLUXSEG6EI8V (X10), V4, V0, V8 + VLUXSEG6EI16V (X10), V4, V0, V8 + VLUXSEG6EI32V (X10), V4, V0, V8 + VLUXSEG6EI64V (X10), V4, V0, V8 + VLOXSEG6EI8V (X10), V4, V8 + VLOXSEG6EI16V (X10), V4, V8 + VLOXSEG6EI32V (X10), V4, V8 + VLOXSEG6EI64V (X10), V4, V8 + VLOXSEG6EI8V (X10), V4, V0, V8 + VLOXSEG6EI16V (X10), V4, V0, V8 + VLOXSEG6EI32V (X10), V4, V0, V8 + VLOXSEG6EI64V (X10), V4, V0, V8 + VLUXSEG7EI8V (X10), V4, V8 + VLUXSEG7EI16V (X10), V4, V8 + VLUXSEG7EI32V (X10), V4, V8 + VLUXSEG7EI64V (X10), V4, V8 + VLUXSEG7EI8V (X10), V4, V0, V8 + VLUXSEG7EI16V (X10), V4, V0, V8 + VLUXSEG7EI32V (X10), V4, V0, V8 + VLUXSEG7EI64V (X10), V4, V0, V8 + VLUXSEG8EI8V (X10), V4, V8 + VLUXSEG8EI16V (X10), V4, V8 + VLUXSEG8EI32V (X10), V4, V8 + VLUXSEG8EI64V (X10), V4, V8 + VLUXSEG8EI8V (X10), V4, V0, V8 + VLUXSEG8EI16V (X10), V4, V0, V8 + VLUXSEG8EI32V (X10), V4, V0, V8 + VLUXSEG8EI64V (X10), V4, V0, V8 + VSUXSEG2EI8V V24, V4, (X10) + VSUXSEG2EI16V V24, V4, (X10) + VSUXSEG2EI32V V24, V4, (X10) + VSUXSEG2EI64V V24, V4, (X10) + VSUXSEG2EI8V V24, V4, V0, (X10) + VSUXSEG2EI16V V24, V4, V0, (X10) + VSUXSEG2EI32V V24, V4, V0, (X10) + VSUXSEG2EI64V V24, V4, V0, (X10) + VSUXSEG3EI8V V24, V4, (X10) + VSUXSEG3EI16V V24, V4, (X10) + VSUXSEG3EI32V V24, V4, (X10) + VSUXSEG3EI64V V24, V4, (X10) + VSUXSEG3EI8V V24, V4, V0, (X10) + VSUXSEG3EI16V V24, V4, V0, (X10) + VSUXSEG3EI32V V24, V4, V0, (X10) + VSUXSEG3EI64V V24, V4, V0, (X10) + VSUXSEG4EI8V V24, V4, (X10) + VSUXSEG4EI16V V24, V4, (X10) + VSUXSEG4EI32V V24, V4, (X10) + VSUXSEG4EI64V V24, V4, (X10) + VSUXSEG4EI8V V24, V4, V0, (X10) + VSUXSEG4EI16V V24, V4, V0, (X10) + VSUXSEG4EI32V V24, V4, V0, (X10) + VSUXSEG4EI64V V24, V4, V0, (X10) + VSUXSEG5EI8V V24, V4, (X10) + VSUXSEG5EI16V V24, V4, (X10) + VSUXSEG5EI32V V24, V4, (X10) + VSUXSEG5EI64V V24, V4, (X10) + VSUXSEG5EI8V V24, V4, V0, (X10) + VSUXSEG5EI16V V24, V4, V0, (X10) + VSUXSEG5EI32V V24, V4, V0, (X10) + VSUXSEG5EI64V V24, V4, V0, (X10) + VSUXSEG6EI8V V24, V4, (X10) + VSUXSEG6EI16V V24, V4, (X10) + VSUXSEG6EI32V V24, V4, (X10) + VSUXSEG6EI64V V24, V4, (X10) + VSUXSEG6EI8V V24, V4, V0, (X10) + VSUXSEG6EI16V V24, V4, V0, (X10) + VSUXSEG6EI32V V24, V4, V0, (X10) + VSUXSEG6EI64V V24, V4, V0, (X10) + VSUXSEG7EI8V V24, V4, (X10) + VSUXSEG7EI16V V24, V4, (X10) + VSUXSEG7EI32V V24, V4, (X10) + VSUXSEG7EI64V V24, V4, (X10) + VSUXSEG7EI8V V24, V4, V0, (X10) + VSUXSEG7EI16V V24, V4, V0, (X10) + VSUXSEG7EI32V V24, V4, V0, (X10) + VSUXSEG7EI64V V24, V4, V0, (X10) + VSUXSEG8EI8V V24, V4, (X10) + VSUXSEG8EI16V V24, V4, (X10) + VSUXSEG8EI32V V24, V4, (X10) + VSUXSEG8EI64V V24, V4, (X10) + VSUXSEG8EI8V V24, V4, V0, (X10) + VSUXSEG8EI16V V24, V4, V0, (X10) + VSUXSEG8EI32V V24, V4, V0, (X10) + VSUXSEG8EI64V V24, V4, V0, (X10) + VLOXSEG2EI8V (X10), V4, V8 + VLOXSEG2EI16V (X10), V4, V8 + VLOXSEG2EI32V (X10), V4, V8 + VLOXSEG2EI64V (X10), V4, V8 + VLOXSEG2EI8V (X10), V4, V0, V8 + VLOXSEG2EI16V (X10), V4, V0, V8 + VLOXSEG2EI32V (X10), V4, V0, V8 + VLOXSEG2EI64V (X10), V4, V0, V8 + VLOXSEG3EI8V (X10), V4, V8 + VLOXSEG3EI16V (X10), V4, V8 + VLOXSEG3EI32V (X10), V4, V8 + VLOXSEG3EI64V (X10), V4, V8 + VLOXSEG3EI8V (X10), V4, V0, V8 + VLOXSEG3EI16V (X10), V4, V0, V8 + VLOXSEG3EI32V (X10), V4, V0, V8 + VLOXSEG3EI64V (X10), V4, V0, V8 + VLOXSEG4EI8V (X10), V4, V8 + VLOXSEG4EI16V (X10), V4, V8 + VLOXSEG4EI32V (X10), V4, V8 + VLOXSEG4EI64V (X10), V4, V8 + VLOXSEG4EI8V (X10), V4, V0, V8 + VLOXSEG4EI16V (X10), V4, V0, V8 + VLOXSEG4EI32V (X10), V4, V0, V8 + VLOXSEG4EI64V (X10), V4, V0, V8 + VLOXSEG5EI8V (X10), V4, V8 + VLOXSEG5EI16V (X10), V4, V8 + VLOXSEG5EI32V (X10), V4, V8 + VLOXSEG5EI64V (X10), V4, V8 + VLOXSEG5EI8V (X10), V4, V0, V8 + VLOXSEG5EI16V (X10), V4, V0, V8 + VLOXSEG5EI32V (X10), V4, V0, V8 + VLOXSEG5EI64V (X10), V4, V0, V8 + VLOXSEG7EI8V (X10), V4, V8 + VLOXSEG7EI16V (X10), V4, V8 + VLOXSEG7EI32V (X10), V4, V8 + VLOXSEG7EI64V (X10), V4, V8 + VLOXSEG7EI8V (X10), V4, V0, V8 + VLOXSEG7EI16V (X10), V4, V0, V8 + VLOXSEG7EI32V (X10), V4, V0, V8 + VLOXSEG7EI64V (X10), V4, V0, V8 + VLOXSEG8EI8V (X10), V4, V8 + VLOXSEG8EI16V (X10), V4, V8 + VLOXSEG8EI32V (X10), V4, V8 + VLOXSEG8EI64V (X10), V4, V8 + VLOXSEG8EI8V (X10), V4, V0, V8 + VLOXSEG8EI16V (X10), V4, V0, V8 + VLOXSEG8EI32V (X10), V4, V0, V8 + VLOXSEG8EI64V (X10), V4, V0, V8 + VSOXSEG2EI8V V24, V4, (X10) + VSOXSEG2EI16V V24, V4, (X10) + VSOXSEG2EI32V V24, V4, (X10) + VSOXSEG2EI64V V24, V4, (X10) + VSOXSEG2EI8V V24, V4, V0, (X10) + VSOXSEG2EI16V V24, V4, V0, (X10) + VSOXSEG2EI32V V24, V4, V0, (X10) + VSOXSEG2EI64V V24, V4, V0, (X10) + VSOXSEG3EI8V V24, V4, (X10) + VSOXSEG3EI16V V24, V4, (X10) + VSOXSEG3EI32V V24, V4, (X10) + VSOXSEG3EI64V V24, V4, (X10) + VSOXSEG3EI8V V24, V4, V0, (X10) + VSOXSEG3EI16V V24, V4, V0, (X10) + VSOXSEG3EI32V V24, V4, V0, (X10) + VSOXSEG3EI64V V24, V4, V0, (X10) + VSOXSEG4EI8V V24, V4, (X10) + VSOXSEG4EI16V V24, V4, (X10) + VSOXSEG4EI32V V24, V4, (X10) + VSOXSEG4EI64V V24, V4, (X10) + VSOXSEG4EI8V V24, V4, V0, (X10) + VSOXSEG4EI16V V24, V4, V0, (X10) + VSOXSEG4EI32V V24, V4, V0, (X10) + VSOXSEG4EI64V V24, V4, V0, (X10) + VSOXSEG5EI8V V24, V4, (X10) + VSOXSEG5EI16V V24, V4, (X10) + VSOXSEG5EI32V V24, V4, (X10) + VSOXSEG5EI64V V24, V4, (X10) + VSOXSEG5EI8V V24, V4, V0, (X10) + VSOXSEG5EI16V V24, V4, V0, (X10) + VSOXSEG5EI32V V24, V4, V0, (X10) + VSOXSEG5EI64V V24, V4, V0, (X10) + VSOXSEG6EI8V V24, V4, (X10) + VSOXSEG6EI16V V24, V4, (X10) + VSOXSEG6EI32V V24, V4, (X10) + VSOXSEG6EI64V V24, V4, (X10) + VSOXSEG6EI8V V24, V4, V0, (X10) + VSOXSEG6EI16V V24, V4, V0, (X10) + VSOXSEG6EI32V V24, V4, V0, (X10) + VSOXSEG6EI64V V24, V4, V0, (X10) + VSOXSEG7EI8V V24, V4, (X10) + VSOXSEG7EI16V V24, V4, (X10) + VSOXSEG7EI32V V24, V4, (X10) + VSOXSEG7EI64V V24, V4, (X10) + VSOXSEG7EI8V V24, V4, V0, (X10) + VSOXSEG7EI16V V24, V4, V0, (X10) + VSOXSEG7EI32V V24, V4, V0, (X10) + VSOXSEG7EI64V V24, V4, V0, (X10) + VSOXSEG8EI8V V24, V4, (X10) + VSOXSEG8EI16V V24, V4, (X10) + VSOXSEG8EI32V V24, V4, (X10) + VSOXSEG8EI64V V24, V4, (X10) + VSOXSEG8EI8V V24, V4, V0, (X10) + VSOXSEG8EI16V V24, V4, V0, (X10) + VSOXSEG8EI32V V24, V4, V0, (X10) + VSOXSEG8EI64V V24, V4, V0, (X10) + + VL1RV (X10), V3 + VL1RE8V (X10), V3 + VL1RE16V (X10), V3 + VL1RE32V (X10), V3 + VL1RE64V (X10), V3 + VL2RV (X10), V2 + VL2RE8V (X10), V2 + VL2RE16V (X10), V2 + VL2RE32V (X10), V2 + VL2RE64V (X10), V2 + VL4RV (X10), V4 + VL4RE8V (X10), V4 + VL4RE16V (X10), V4 + VL4RE32V (X10), V4 + VL4RE64V (X10), V4 + VL8RV (X10), V8 + VL8RE8V (X10), V8 + VL8RE16V (X10), V8 + VL8RE32V (X10), V8 + VL8RE64V (X10), V8 + VS1RV V3, (X11) + VS2RV V2, (X11) + VS4RV V4, (X11) + VS8RV V8, (X11) + + RET +` + dir := t.TempDir() + path := filepath.Join(dir, "vecls_riscv64.s") + if err := os.WriteFile(path, []byte(src), 0o644); err != nil { + t.Fatal(err) + } + assertRISCVDifferential(t, path, src, "vecls") +}