feat(asm): encode the riscv64 vector arithmetic families
Assisted-by: GLM 5.3 Flash
This commit is contained in:
1 parent
5de9e985f8
commit
778c297214
3 files changed
+1812
-102
No files matched your search
+564
-81
@@ -3636,88 +3636,571 @@ func encodeRISCVVector(mnem string, ops []*ast.Operand) ([]byte, bool, error) {
|
||||
// (0xcd07f657) from GOARCH=riscv64 go tool asm.
|
||||
ivli := mnem == "VSETIVLI" || isImmOperand(ops[0])
|
||||
return wordLE(riscvVSetEnc(ivli, avl, riscvVType(vsew, vlmul, vta, vma), rd)), true, nil
|
||||
|
||||
case "VADDVV", "VXORVV", "VMSNEVV":
|
||||
// Vector-vector: INSTR vs1, vs2, vd.
|
||||
if len(ops) != 3 {
|
||||
return nil, true, fmt.Errorf("%s expects 3 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
vs1, vs2, vd := reg(ops[0]), reg(ops[1]), reg(ops[2])
|
||||
if vs1 < 0 || vs2 < 0 || vd < 0 {
|
||||
return nil, true, fmt.Errorf("%s: invalid vector register", mnem)
|
||||
}
|
||||
funct6 := map[string]int{"VADDVV": 0x00, "VXORVV": 0x0B, "VMSNEVV": 0x19}[mnem]
|
||||
return wordLE(riscvVVInstr(funct6, riscvVf3VV, int32(vs1), vs2, vd)), true, nil
|
||||
|
||||
case "VADDVX", "VMSEQVX":
|
||||
// Vector-scalar: INSTR rs1, vs2, vd (the scalar in the rs1 field).
|
||||
if len(ops) != 3 {
|
||||
return nil, true, fmt.Errorf("%s expects 3 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
rs1, vs2, vd := reg(ops[0]), reg(ops[1]), reg(ops[2])
|
||||
if rs1 < 0 || vs2 < 0 || vd < 0 {
|
||||
return nil, true, fmt.Errorf("%s: invalid register operand", mnem)
|
||||
}
|
||||
funct6 := 0x00
|
||||
if mnem == "VMSEQVX" {
|
||||
funct6 = 0x18
|
||||
}
|
||||
return wordLE(riscvVVInstr(funct6, riscvVf3VX, int32(rs1), vs2, vd)), true, nil
|
||||
|
||||
case "VSLLVI", "VSRLVI":
|
||||
// Vector-immediate shift: INSTR $uimm, vs2, vd.
|
||||
if len(ops) != 3 {
|
||||
return nil, true, fmt.Errorf("%s expects 3 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
imm := int(immFromOperand(ops[0]))
|
||||
if imm < 0 || imm > 31 {
|
||||
return nil, true, fmt.Errorf("%s: immediate out of range [0, 31]", mnem)
|
||||
}
|
||||
vs2, vd := reg(ops[1]), reg(ops[2])
|
||||
if vs2 < 0 || vd < 0 {
|
||||
return nil, true, fmt.Errorf("%s: invalid vector register", mnem)
|
||||
}
|
||||
funct6 := 0x25 // vsll.vi
|
||||
if mnem == "VSRLVI" {
|
||||
funct6 = 0x28 // vsrl.vi
|
||||
}
|
||||
return wordLE(riscvVVInstr(funct6, riscvVf3VI, int32(imm), vs2, vd)), true, nil
|
||||
|
||||
case "VFIRSTM":
|
||||
// vmfirst.m rd, vs2: the unmasked form carries 0x11 in the rs1 field
|
||||
// and sets the mask bit (funct7 = 0x20 | 1).
|
||||
if len(ops) != 2 {
|
||||
return nil, true, fmt.Errorf("VFIRSTM expects 2 operands, got %d", len(ops))
|
||||
}
|
||||
vs2, rd := reg(ops[0]), reg(ops[1])
|
||||
if vs2 < 0 || rd < 0 {
|
||||
return nil, true, fmt.Errorf("VFIRSTM: invalid register operand")
|
||||
}
|
||||
return wordLE(riscvVUnaryInstr(0x10, riscvVf3MV, 0x11, vs2, rd)), true, nil
|
||||
|
||||
case "VIDV":
|
||||
// vid.v vd (vs2 must be v0; the unmasked form sets the mask bit).
|
||||
if len(ops) != 1 {
|
||||
return nil, true, fmt.Errorf("VIDV expects 1 operand, got %d", len(ops))
|
||||
}
|
||||
vd := reg(ops[0])
|
||||
if vd < 0 {
|
||||
return nil, true, fmt.Errorf("VIDV: invalid vector register")
|
||||
}
|
||||
return wordLE(riscvVUnaryInstr(0x14, riscvVf3MV, 0x11, 0, vd)), true, nil
|
||||
|
||||
case "VMV4RV":
|
||||
// vmv4r.v vd, vs2: whole-register group move.
|
||||
if len(ops) != 2 {
|
||||
return nil, true, fmt.Errorf("VMV4RV expects 2 operands, got %d", len(ops))
|
||||
}
|
||||
vs2, vd := reg(ops[0]), reg(ops[1])
|
||||
if vs2 < 0 || vd < 0 {
|
||||
return nil, true, fmt.Errorf("VMV4RV: invalid vector register")
|
||||
}
|
||||
return wordLE(riscvVUnaryInstr(0x27, 0x3, 0x3, vs2, vd)), true, nil
|
||||
}
|
||||
return nil, false, nil
|
||||
// Every remaining OP-V mnemonic the toolchain knows: the arithmetic
|
||||
// table, dispatched by operand class.
|
||||
return encodeRISCVVecOp(mnem, ops)
|
||||
}
|
||||
|
||||
// encodeRISCVVecOp encodes one vector arithmetic instruction through the
|
||||
// extracted table. The entry's class places the operands in the rs1 and vs2
|
||||
// fields, an optional V0 between the sources and the destination clears the
|
||||
// vm bit, and the transform classes rewrite the pseudo forms the toolchain
|
||||
// expands before encoding (the swapped comparisons, VNEGV and friends).
|
||||
func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) {
|
||||
op, ok := riscvVecOps[mnem]
|
||||
if !ok {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
// vecReg reads a register operand; what names the field in the error.
|
||||
vecReg := func(o *ast.Operand, what string) (int, error) {
|
||||
r := regFromOperand(o)
|
||||
if r < 0 {
|
||||
return 0, fmt.Errorf("%s: invalid %s", mnem, what)
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
// vecImm reads the immediate the entry's form bounds: signed five bits
|
||||
// [-16, 15], or unsigned [0, 31] for the shifts and slides.
|
||||
vecImm := func(o *ast.Operand) (int32, error) {
|
||||
if !isImmOperand(o) {
|
||||
return 0, fmt.Errorf("%s expects an immediate first operand", mnem)
|
||||
}
|
||||
v := immFromOperand(o)
|
||||
if op.immU {
|
||||
if v < 0 || v > 31 {
|
||||
return 0, fmt.Errorf("%s: immediate %d out of range [0, 31]", mnem, v)
|
||||
}
|
||||
} else if v < -16 || v > 15 {
|
||||
return 0, fmt.Errorf("%s: immediate %d out of range [-16, 15]", mnem, v)
|
||||
}
|
||||
return int32(v), nil
|
||||
}
|
||||
// vecMask reads the optional mask operand: only V0 is lawful.
|
||||
vecMask := func(o *ast.Operand) error {
|
||||
if regFromOperand(o) != 0 {
|
||||
return fmt.Errorf("%s: the vector mask register must be V0", mnem)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// vm carries the funct7 with the vm bit set for the unmasked form: the
|
||||
// toolchain ORs 1 when no V0 follows the sources.
|
||||
vm := func(masked bool) uint32 {
|
||||
if !masked {
|
||||
return op.funct7 | 1
|
||||
}
|
||||
return op.funct7
|
||||
}
|
||||
// word builds the instruction from the entry's fields.
|
||||
word := func(funct7 uint32, rs1Field int32, vs2 int, funct3 uint32, vd int) ([]byte, bool, error) {
|
||||
return wordLE(riscvVecWord(funct7, rs1Field, vs2, funct3, vd)), true, nil
|
||||
}
|
||||
// rename resolves a transform to its target table entry.
|
||||
rename := func(to string) (riscvVecOp, error) {
|
||||
t, ok := riscvVecOps[to]
|
||||
if !ok {
|
||||
return riscvVecOp{}, fmt.Errorf("%s: transform target %q not in the table", mnem, to)
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
switch op.class {
|
||||
case vecVV:
|
||||
// INSTR vs1|$imm, vs2 [, V0], vd.
|
||||
if len(ops) != 3 && len(ops) != 4 {
|
||||
return nil, true, fmt.Errorf("%s expects 3 or 4 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
masked := len(ops) == 4
|
||||
if masked {
|
||||
if err := vecMask(ops[2]); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
}
|
||||
vs2, err := vecReg(ops[1], "vs2")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vd, err := vecReg(ops[len(ops)-1], "vd")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
var rs1Field int32
|
||||
if op.imm {
|
||||
if rs1Field, err = vecImm(ops[0]); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
} else {
|
||||
var vs1 int
|
||||
if vs1, err = vecReg(ops[0], "vs1"); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
rs1Field = int32(vs1)
|
||||
}
|
||||
return word(vm(masked), rs1Field, vs2, op.funct3, vd)
|
||||
|
||||
case vecMACC:
|
||||
// INSTR vs2, vs1 [, V0], vd: the multiply-accumulate order, the
|
||||
// addend in the rs1 field and the multiplicand in vs2.
|
||||
if len(ops) != 3 && len(ops) != 4 {
|
||||
return nil, true, fmt.Errorf("%s expects 3 or 4 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
masked := len(ops) == 4
|
||||
if masked {
|
||||
if err := vecMask(ops[2]); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
}
|
||||
vs2, err := vecReg(ops[0], "vs2")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vd, err := vecReg(ops[len(ops)-1], "vd")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
var rs1Field int32
|
||||
if op.imm {
|
||||
if rs1Field, err = vecImm(ops[1]); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
} else {
|
||||
var vs1 int
|
||||
if vs1, err = vecReg(ops[1], "vs1"); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
rs1Field = int32(vs1)
|
||||
}
|
||||
return word(vm(masked), rs1Field, vs2, op.funct3, vd)
|
||||
|
||||
case vecSWAPVV:
|
||||
// VMSGT*/VMSGE*/VMFGT*/VMFGE* swap the two sources and lower to the
|
||||
// VMSLT*/VMSLE*/VMFLT*/VMFLE* entries the table carries.
|
||||
if len(ops) != 3 && len(ops) != 4 {
|
||||
return nil, true, fmt.Errorf("%s expects 3 or 4 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
masked := len(ops) == 4
|
||||
if masked {
|
||||
if err := vecMask(ops[2]); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
}
|
||||
t, err := rename(map[string]string{
|
||||
"VMSGTVV": "VMSLTVV", "VMSGTUVV": "VMSLTUVV",
|
||||
"VMSGEVV": "VMSLEVV", "VMSGEUVV": "VMSLEUVV",
|
||||
"VMFGTVV": "VMFLTVV", "VMFGEVV": "VMFLEVV",
|
||||
}[mnem])
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vs2, err := vecReg(ops[0], "vs2")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vd, err := vecReg(ops[len(ops)-1], "vd")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vs1, err := vecReg(ops[1], "vs1")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
f7 := t.funct7
|
||||
if !masked {
|
||||
f7 |= 1
|
||||
}
|
||||
return word(f7, int32(vs1), vs2, t.funct3, vd)
|
||||
|
||||
case vecSWAPVI:
|
||||
// VMSLTVI and the VMSGE*VI forms subtract one from the immediate and
|
||||
// lower to the VMSLE*/VMSGT* entries.
|
||||
if len(ops) != 3 && len(ops) != 4 {
|
||||
return nil, true, fmt.Errorf("%s expects 3 or 4 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
masked := len(ops) == 4
|
||||
if masked {
|
||||
if err := vecMask(ops[2]); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
}
|
||||
t, err := rename(map[string]string{
|
||||
"VMSLTVI": "VMSLEVI", "VMSLTUVI": "VMSLEUVI",
|
||||
"VMSGEVI": "VMSGTVI", "VMSGEUVI": "VMSGTUVI",
|
||||
}[mnem])
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
imm, err := vecImm(ops[0])
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
imm--
|
||||
if imm < -16 || imm > 15 {
|
||||
return nil, true, fmt.Errorf("%s: immediate %d leaves [-16, 15] after the swap", mnem, imm+1)
|
||||
}
|
||||
vs2, err := vecReg(ops[1], "vs2")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vd, err := vecReg(ops[len(ops)-1], "vd")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
f7 := t.funct7
|
||||
if !masked {
|
||||
f7 |= 1
|
||||
}
|
||||
return word(f7, imm, vs2, t.funct3, vd)
|
||||
|
||||
case vecUNARY, vecM2I:
|
||||
// INSTR vs2 [, V0], vd: one vector source, the fixed rs1 field; the
|
||||
// m2i members take the destination in the integer file.
|
||||
if len(ops) != 2 && len(ops) != 3 {
|
||||
return nil, true, fmt.Errorf("%s expects 2 or 3 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
masked := len(ops) == 3
|
||||
if masked {
|
||||
if err := vecMask(ops[1]); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
}
|
||||
vs2, err := vecReg(ops[0], "vs2")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vd, err := vecReg(ops[len(ops)-1], "vd")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
return word(vm(masked), int32(op.rs1), vs2, op.funct3, vd)
|
||||
|
||||
case vecNEG:
|
||||
// VNEGV, VWCVTXXV, VWCVTUXXV and VNCVTXXW read as one-operand forms
|
||||
// of VRSUBVX, VWADDVX, VWADDUVX and VNSRLWX with X0 in the rs1 field.
|
||||
if len(ops) != 2 && len(ops) != 3 {
|
||||
return nil, true, fmt.Errorf("%s expects 2 or 3 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
masked := len(ops) == 3
|
||||
if masked {
|
||||
if err := vecMask(ops[1]); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
}
|
||||
t, err := rename(map[string]string{
|
||||
"VNEGV": "VRSUBVX", "VWCVTXXV": "VWADDVX",
|
||||
"VWCVTUXXV": "VWADDUVX", "VNCVTXXW": "VNSRLWX",
|
||||
}[mnem])
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vs2, err := vecReg(ops[0], "vs2")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vd, err := vecReg(ops[len(ops)-1], "vd")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
f7 := t.funct7
|
||||
if !masked {
|
||||
f7 |= 1
|
||||
}
|
||||
return word(f7, 0, vs2, t.funct3, vd)
|
||||
|
||||
case vecVNOT:
|
||||
// VNOTV reads as VXORVI with the all-ones immediate.
|
||||
if len(ops) != 2 && len(ops) != 3 {
|
||||
return nil, true, fmt.Errorf("%s expects 2 or 3 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
masked := len(ops) == 3
|
||||
if masked {
|
||||
if err := vecMask(ops[1]); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
}
|
||||
t, err := rename("VXORVI")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vs2, err := vecReg(ops[0], "vs2")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vd, err := vecReg(ops[len(ops)-1], "vd")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
f7 := t.funct7
|
||||
if !masked {
|
||||
f7 |= 1
|
||||
}
|
||||
return word(f7, -1, vs2, t.funct3, vd)
|
||||
|
||||
case vecVFABS:
|
||||
// VFABSV and VFNEGV read as VFSGNJXVV/VFSGNJNVVV with the source in
|
||||
// both the rs1 and vs2 fields.
|
||||
if len(ops) != 2 && len(ops) != 3 {
|
||||
return nil, true, fmt.Errorf("%s expects 2 or 3 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
masked := len(ops) == 3
|
||||
if masked {
|
||||
if err := vecMask(ops[1]); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
}
|
||||
t, err := rename(map[string]string{
|
||||
"VFABSV": "VFSGNJXVV", "VFNEGV": "VFSGNJNVV",
|
||||
}[mnem])
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vs2, err := vecReg(ops[0], "vs2")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vd, err := vecReg(ops[len(ops)-1], "vd")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
f7 := t.funct7
|
||||
if !masked {
|
||||
f7 |= 1
|
||||
}
|
||||
return word(f7, int32(vs2), vs2, t.funct3, vd)
|
||||
|
||||
case vecVMVV:
|
||||
// INSTR vs2, vd (vmv.v.v/vmv.v.x): the source in the rs1 field, V0
|
||||
// fixed in vs2, the vm bit from the table.
|
||||
if len(ops) != 2 {
|
||||
return nil, true, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
vs2, err := vecReg(ops[0], "vs2")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vd, err := vecReg(ops[1], "vd")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
return word(op.funct7, int32(vs2), 0, op.funct3, vd)
|
||||
|
||||
case vecVMVI:
|
||||
// INSTR $imm, vd (vmv.v.i): the immediate in the rs1 field, V0 in
|
||||
// vs2, the vm bit from the table.
|
||||
if len(ops) != 2 {
|
||||
return nil, true, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
imm, err := vecImm(ops[0])
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vd, err := vecReg(ops[1], "vd")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
return word(op.funct7, imm, 0, op.funct3, vd)
|
||||
|
||||
case vecVFMVVF:
|
||||
// INSTR fs1, vd (vfmv.v.f): the scalar in the rs1 field, V0 in vs2.
|
||||
if len(ops) != 2 {
|
||||
return nil, true, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
fs1, err := vecReg(ops[0], "fs1")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vd, err := vecReg(ops[1], "vd")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
return word(op.funct7, int32(fs1), 0, op.funct3, vd)
|
||||
|
||||
case vecTWO:
|
||||
// INSTR vs2, vd: two-operand forms with the fixed rs1 field (the
|
||||
// extensions and conversions, the whole-register moves, the scalar
|
||||
// reads).
|
||||
if len(ops) != 2 {
|
||||
return nil, true, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
vs2, err := vecReg(ops[0], "vs2")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vd, err := vecReg(ops[1], "vd")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
return word(op.funct7, int32(op.rs1), vs2, op.funct3, vd)
|
||||
|
||||
case vecTWOX:
|
||||
// INSTR xs1|fs1, vd: two-operand forms with the fixed vs2 field
|
||||
// (vmv.s.x and vfmv.s.f).
|
||||
if len(ops) != 2 {
|
||||
return nil, true, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
rs1, err := vecReg(ops[0], "rs1")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vd, err := vecReg(ops[1], "vd")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
return word(op.funct7, int32(rs1), int(op.rs1), op.funct3, vd)
|
||||
|
||||
case vecADC:
|
||||
// INSTR vs1|$imm, vs2, V0, vd: the carry forms, the mask mandatory,
|
||||
// V0 rejected as the destination.
|
||||
if len(ops) != 4 {
|
||||
return nil, true, fmt.Errorf("%s expects 4 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
if err := vecMask(ops[2]); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vs2, err := vecReg(ops[1], "vs2")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vd, err := vecReg(ops[3], "vd")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
if vd == 0 {
|
||||
return nil, true, fmt.Errorf("%s: invalid destination register V0", mnem)
|
||||
}
|
||||
var rs1Field int32
|
||||
if op.imm {
|
||||
if rs1Field, err = vecImm(ops[0]); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
} else {
|
||||
var vs1 int
|
||||
if vs1, err = vecReg(ops[0], "vs1"); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
rs1Field = int32(vs1)
|
||||
}
|
||||
return word(op.funct7, rs1Field, vs2, op.funct3, vd)
|
||||
|
||||
case vecMERGE:
|
||||
// INSTR vs1|fs1|$imm, vs2, V0, vd: the merge forms, the mask
|
||||
// mandatory, V0 allowed as the destination.
|
||||
if len(ops) != 4 {
|
||||
return nil, true, fmt.Errorf("%s expects 4 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
if err := vecMask(ops[2]); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vs2, err := vecReg(ops[1], "vs2")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vd, err := vecReg(ops[3], "vd")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
var rs1Field int32
|
||||
if op.imm {
|
||||
if rs1Field, err = vecImm(ops[0]); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
} else {
|
||||
var vs1 int
|
||||
if vs1, err = vecReg(ops[0], "vs1"); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
rs1Field = int32(vs1)
|
||||
}
|
||||
return word(op.funct7, rs1Field, vs2, op.funct3, vd)
|
||||
|
||||
case vecVMADC:
|
||||
// INSTR vs1|$imm, vs2, vd: the carry-producing forms; the third
|
||||
// operand names the destination and may be V0.
|
||||
if len(ops) != 3 {
|
||||
return nil, true, fmt.Errorf("%s expects 3 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
vs2, err := vecReg(ops[1], "vs2")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vd, err := vecReg(ops[2], "vd")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
var rs1Field int32
|
||||
if op.imm {
|
||||
if rs1Field, err = vecImm(ops[0]); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
} else {
|
||||
var vs1 int
|
||||
if vs1, err = vecReg(ops[0], "vs1"); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
rs1Field = int32(vs1)
|
||||
}
|
||||
return word(op.funct7, rs1Field, vs2, op.funct3, vd)
|
||||
|
||||
case vecMM:
|
||||
// INSTR vs1, vs2, vd: the mask-mask forms. VMMVM and VMNOTM take
|
||||
// two operands and fold the second source into the first; the vm
|
||||
// bit stays as the table carries it.
|
||||
folded := mnem == "VMMVM" || mnem == "VMNOTM"
|
||||
if (folded && len(ops) != 2) || (!folded && len(ops) != 3) {
|
||||
return nil, true, fmt.Errorf("%s expects %d operands, got %d", mnem, map[bool]int{true: 2, false: 3}[folded], len(ops))
|
||||
}
|
||||
vs1, err := vecReg(ops[0], "vs1")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
vs2 := vs1
|
||||
if !folded {
|
||||
if vs2, err = vecReg(ops[1], "vs2"); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
}
|
||||
vd, err := vecReg(ops[len(ops)-1], "vd")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
return word(op.funct7, int32(vs1), vs2, op.funct3, vd)
|
||||
|
||||
case vecVMCLR:
|
||||
// INSTR vd: the whole-mask clears and sets, one register in all
|
||||
// three fields.
|
||||
if len(ops) != 1 {
|
||||
return nil, true, fmt.Errorf("%s expects 1 operand, got %d", mnem, len(ops))
|
||||
}
|
||||
t, err := rename(map[string]string{
|
||||
"VMCLRM": "VMXORMM", "VMSETM": "VMXNORMM",
|
||||
}[mnem])
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
r, err := vecReg(ops[0], "vd")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
return word(t.funct7, int32(r), r, t.funct3, r)
|
||||
|
||||
case vecVID:
|
||||
// INSTR [V0,] vd: the element index, the mask before the destination.
|
||||
if len(ops) != 1 && len(ops) != 2 {
|
||||
return nil, true, fmt.Errorf("%s expects 1 or 2 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
masked := len(ops) == 2
|
||||
if masked {
|
||||
if err := vecMask(ops[0]); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
}
|
||||
vd, err := vecReg(ops[len(ops)-1], "vd")
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
return word(vm(masked), int32(op.rs1), 0, op.funct3, vd)
|
||||
}
|
||||
return nil, true, fmt.Errorf("%s: unhandled vector operand class", mnem)
|
||||
}
|
||||
|
||||
// riscvVTypeToken parses a vsetvli configuration token (E8, M8, MF2 and
|
||||
|
||||
Reference in new issue
Block a user