diff --git a/asm/riscv_assemble.go b/asm/riscv_assemble.go index 5963c9a..383033c 100644 --- a/asm/riscv_assemble.go +++ b/asm/riscv_assemble.go @@ -1142,7 +1142,7 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv return nil, fmt.Errorf("WORD expects an immediate") } if w < 0 || w > 0xFFFFFFFF { - return nil, fmt.Errorf("WORD: immediate %d does not fit a 32-bit word", w) + return nil, fmt.Errorf("WORD: immediate %d must be in range [0x0, 0xffffffff]", w) } return []byte{byte(w), byte(w >> 8), byte(w >> 16), byte(w >> 24)}, nil case "BYTE": @@ -1249,7 +1249,10 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv rd := 0 var target string if len(ops) >= 2 { - rd = regFromOperand(ops[0]) + var err error + if rd, err = riscvWantIntReg(mnem, "rd", ops[0]); err != nil { + return nil, err + } target = labelFromOperand(ops[1]) } else if len(ops) == 1 { target = labelFromOperand(ops[0]) @@ -1294,9 +1297,9 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv if len(ops) != 2 { return nil, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops)) } - rs := regFromOperand(ops[0]) - if rs < 0 { - return nil, fmt.Errorf("%s: invalid register", mnem) + rs, err := riscvWantIntReg(mnem, "rs1", ops[0]) + if err != nil { + return nil, err } targetOff := 0 target := "" @@ -1355,16 +1358,22 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv if mnem == "PAUSE" { imm = 0x010 } + if mnem == "FENCE.TSO" && len(ops) != 0 { + return nil, fmt.Errorf("FENCE.TSO must not have operands") + } // FENCE pred, succ spells both flags with the toolchain's sixteen // IORW combinations, packed as pred<<4 | succ in the immediate. - if mnem == "FENCE" && len(ops) == 2 { + if mnem == "FENCE" && len(ops) > 0 { + if len(ops) != 2 { + return nil, fmt.Errorf("FENCE expects 0 or 2 operands, got %d", len(ops)) + } pred, ok := riscvFenceFlag(ops[0]) if !ok { - return nil, fmt.Errorf("FENCE: invalid predecessor operand %q", ops[0].Raw) + return nil, fmt.Errorf("invalid FENCE predecessor operand %q", ops[0].Raw) } succ, ok := riscvFenceFlag(ops[1]) if !ok { - return nil, fmt.Errorf("FENCE: invalid successor operand %q", ops[1].Raw) + return nil, fmt.Errorf("invalid FENCE successor operand %q", ops[1].Raw) } imm = int32(pred)<<4 | int32(succ) } @@ -1391,10 +1400,13 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv if len(ops) != 2 { return nil, fmt.Errorf("SFENCEVMA expects 2 operands, got %d", len(ops)) } - rs1 := regFromOperand(ops[0]) - rs2 := regFromOperand(ops[1]) - if rs1 < 0 || rs2 < 0 { - return nil, fmt.Errorf("SFENCEVMA: invalid register operand") + rs1, err := riscvWantIntReg(mnem, "rs1", ops[0]) + if err != nil { + return nil, err + } + rs2, err := riscvWantIntReg(mnem, "rs2", ops[1]) + if err != nil { + return nil, err } word = riscvRType(riscvEnc{0x73, 0x0, 0x09}, 0, rs1, rs2) return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil @@ -1406,13 +1418,15 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv if len(ops) != 1 && len(ops) != 2 { return nil, fmt.Errorf("%s expects 1 or 2 operands, got %d", mnem, len(ops)) } - rs := regFromOperand(ops[0]) + rs, err := riscvWantIntReg(mnem, "rs1", ops[0]) + if err != nil { + return nil, err + } rd := rs if len(ops) == 2 { - rd = regFromOperand(ops[1]) - } - if rs < 0 || rd < 0 { - return nil, fmt.Errorf("%s: invalid register operand", mnem) + if rd, err = riscvWantIntReg(mnem, "rd", ops[1]); err != nil { + return nil, err + } } enc := riscvEnc{0x33, 0x0, 0x20} // sub if mnem == "NEGW" { @@ -1428,10 +1442,13 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv if len(ops) != 2 { return nil, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops)) } - rs := regFromOperand(ops[0]) - rd := regFromOperand(ops[1]) - if rs < 0 || rd < 0 { - return nil, fmt.Errorf("%s: invalid register operand", mnem) + rs, err := riscvWantIntReg(mnem, "rs1", ops[0]) + if err != nil { + return nil, err + } + rd, err := riscvWantIntReg(mnem, "rd", ops[1]) + if err != nil { + return nil, err } if mnem == "SEQZ" { word = riscvIType(riscvEnc{0x13, 0x3, 0x00}, rd, rs, 1) // sltiu $1 @@ -1448,10 +1465,14 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv if len(ops) != 2 { return nil, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops)) } - rs1 := regFromOperand(ops[0]) - rd := regFromOperand(ops[1]) - if rd < 0 || rs1 < 0 { - return nil, fmt.Errorf("invalid operand in %s", mnem) + rdBank, rs1Bank := riscvCvtBanks(mnem) + rs1, err := riscvWantRegDescr(mnem, "rs1", rs1Bank.String(), ops[0], rs1Bank, 0, 31) + if err != nil { + return nil, err + } + rd, err := riscvWantRegDescr(mnem, "rd", rdBank.String(), ops[1], rdBank, 0, 31) + if err != nil { + return nil, err } word := riscvCvtType(cvtEnc, rd, rs1) return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil @@ -1469,10 +1490,14 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv if len(ops) != 2 { return nil, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops)) } - rs1 := regFromOperand(ops[0]) - rd := regFromOperand(ops[1]) - if rd < 0 || rs1 < 0 { - return nil, fmt.Errorf("invalid operand in %s", mnem) + rdBank, rs1Bank := riscvCvtBanks(mnem[:i]) + rs1, err := riscvWantRegDescr(mnem, "rs1", rs1Bank.String(), ops[0], rs1Bank, 0, 31) + if err != nil { + return nil, err + } + rd, err := riscvWantRegDescr(mnem, "rd", rdBank.String(), ops[1], rdBank, 0, 31) + if err != nil { + return nil, err } base.funct3 = uint32(rm) word := riscvCvtType(base, rd, rs1) @@ -1485,12 +1510,21 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv if len(ops) != 4 { return nil, fmt.Errorf("%s expects 4 operands, got %d", mnem, len(ops)) } - rs1 := regFromOperand(ops[0]) - rs2 := regFromOperand(ops[1]) - rs3 := regFromOperand(ops[2]) - rd := regFromOperand(ops[3]) - if rd < 0 || rs1 < 0 || rs2 < 0 || rs3 < 0 { - return nil, fmt.Errorf("invalid FP register in %s", mnem) + rs1, err := riscvWantFloatReg(mnem, "rs1", ops[0]) + if err != nil { + return nil, err + } + rs2, err := riscvWantFloatReg(mnem, "rs2", ops[1]) + if err != nil { + return nil, err + } + rs3, err := riscvWantFloatReg(mnem, "rs3", ops[2]) + if err != nil { + return nil, err + } + rd, err := riscvWantFloatReg(mnem, "rd", ops[3]) + if err != nil { + return nil, err } word := riscvFmaType(fmaEnc, rd, rs1, rs2, rs3) return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil @@ -1530,8 +1564,8 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv if csrPseudo && !csrRead && len(ops) != 2 { return nil, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops)) } - if !csrPseudo && len(ops) != 3 { - return nil, fmt.Errorf("%s expects 3 operands, got %d", mnem, len(ops)) + if !csrPseudo && len(ops) != 3 && len(ops) != 2 { + return nil, fmt.Errorf("%s expects 2 or 3 operands, got %d", mnem, len(ops)) } csrOp := ops[0] srcOp := ops[0] @@ -1560,6 +1594,18 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv }[mnem] } csrEnc = riscvCsrTable[csrMnem] + case len(ops) == 2: + // The read form of a full CSR name: CSRRW csr, rd. One operand + // must name a CSR; neither does is the toolchain's "missing CSR + // name". + if _, ok := riscvCSRAddress(ops[0]); ok { + rdOp = ops[1] + } else if _, ok := riscvCSRAddress(ops[1]); ok { + csrOp, rdOp = ops[1], ops[0] + } else { + return nil, fmt.Errorf("%s: missing CSR name", mnem) + } + csrRead = true default: // Either src, csr, rd or csr, src, rd: a CSR *name* in the // second operand marks the toolchain's order. @@ -1581,6 +1627,11 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv } } } + // The source takes an integer register or an immediate: a memory + // operand is the toolchain's first-operand rejection. + if !csrRead && len(ops) > 0 && isMemOperand(srcOp) { + return nil, fmt.Errorf("%s: integer register or immediate expected for 1st operand", mnem) + } csr, ok := riscvCSRAddress(csrOp) if !ok && csrFix == 0 { return nil, fmt.Errorf("%s: unknown CSR %q", mnem, csrOp.Raw) @@ -1592,16 +1643,15 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv return nil, fmt.Errorf("%s: CSR address %d out of range 0-0xFFF", mnem, csr) } rd := 0 - if !csrPseudo { - rd = regFromOperand(rdOp) // destination register - if rd < 0 { - return nil, fmt.Errorf("invalid destination register in %s", mnem) + if !csrPseudo || csrRead { + // The destination takes an integer register: a memory operand + // is the toolchain's output rejection. + if rdOp != nil && isMemOperand(rdOp) { + return nil, fmt.Errorf("%s: needs an integer register output", mnem) } - } - if csrRead { - rd = regFromOperand(rdOp) - if rd < 0 { - return nil, fmt.Errorf("invalid destination register in %s", mnem) + var err error + if rd, err = riscvWantIntReg(mnem, "rd", rdOp); err != nil { + return nil, err } } var src int @@ -1612,15 +1662,15 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv // Immediate variant: the source is a 5-bit unsigned immediate. src = int(immFromOperand(srcOp)) if src < 0 || src > 31 { - return nil, fmt.Errorf("%s: uimm out of range 0-31", mnem) + return nil, fmt.Errorf("%s: immediate %d out of range 0 to 31", mnem, src) } case csrEnc.imm: return nil, fmt.Errorf("%s expects an immediate source", mnem) default: - // Register variant: the source is a register. - src = regFromOperand(srcOp) - if src < 0 { - return nil, fmt.Errorf("invalid source register in %s", mnem) + // Register variant: the source is an integer register. + var err error + if src, err = riscvWantIntReg(mnem, "rs1", srcOp); err != nil { + return nil, err } } word := riscvCsrType(csrEnc, rd, src, csr) @@ -1646,19 +1696,28 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv // R-type: Go reverses the ISA order, writing rs2, rs1, rd (destination // last); the two-operand form INSTR rs2, rd uses rd as rs1. case len(ops) == 3 && isRTypeInstr(mnem): - rs2 := regFromOperand(ops[0]) // first operand = rs2 - rs1 := regFromOperand(ops[1]) // second operand = rs1 - rd := regFromOperand(ops[2]) // destination (last operand) - if rd < 0 || rs1 < 0 || rs2 < 0 { - return nil, fmt.Errorf("invalid register in %s", mnem) + rs2, err := riscvWantIntReg(mnem, "rs2", ops[0]) // first operand = rs2 + if err != nil { + return nil, err + } + rs1, err := riscvWantIntReg(mnem, "rs1", ops[1]) // second operand = rs1 + if err != nil { + return nil, err + } + rd, err := riscvWantIntReg(mnem, "rd", ops[2]) // destination (last operand) + if err != nil { + return nil, err } word = riscvRType(enc, rd, rs1, rs2) case len(ops) == 2 && isRTypeInstr(mnem): - rs2 := regFromOperand(ops[0]) // source (first operand) - rd := regFromOperand(ops[1]) // destination (second operand) - if rd < 0 || rs2 < 0 { - return nil, fmt.Errorf("invalid register in %s", mnem) + rs2, err := riscvWantIntReg(mnem, "rs2", ops[0]) // source (first operand) + if err != nil { + return nil, err + } + rd, err := riscvWantIntReg(mnem, "rd", ops[1]) // destination (second operand) + if err != nil { + return nil, err } word = riscvRType(enc, rd, rd, rs2) @@ -1674,10 +1733,13 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv if hi := riscvShiftMax(mnem); shamt < 0 || shamt > hi { return nil, fmt.Errorf("%s: immediate %d out of range 0 to %d", mnem, shamt, hi) } - rs1 := regFromOperand(ops[1]) - rd := regFromOperand(ops[2]) - if rd < 0 || rs1 < 0 { - return nil, fmt.Errorf("invalid register in %s", mnem) + rs1, err := riscvWantIntReg(mnem, "rs1", ops[1]) + if err != nil { + return nil, err + } + rd, err := riscvWantIntReg(mnem, "rd", ops[2]) + if err != nil { + return nil, err } word = riscvRType(enc, rd, rs1, int(shamt)) @@ -1689,19 +1751,25 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv if hi := riscvShiftMax(mnem); shamt < 0 || shamt > hi { return nil, fmt.Errorf("%s: immediate %d out of range 0 to %d", mnem, shamt, hi) } - rd := regFromOperand(ops[1]) - if rd < 0 { - return nil, fmt.Errorf("invalid register in %s", mnem) + rd, err := riscvWantIntReg(mnem, "rd", ops[1]) + if err != nil { + return nil, err } word = riscvRType(enc, rd, rd, int(shamt)) // AMO atomics: Plan 9 order is INSTR src, (addr), dst. case len(ops) == 3 && isAMOInstr(mnem): - rs2 := regFromOperand(ops[0]) // source value + rs2, err := riscvWantIntReg(mnem, "rs2", ops[0]) // source value + if err != nil { + return nil, err + } rs1, _ := memFromOperandWithFrame(ops[1], fi) // memory address - rd := regFromOperand(ops[2]) // destination (old value) - if rd < 0 || rs1 < 0 || rs2 < 0 { - return nil, fmt.Errorf("invalid operand in %s", mnem) + rd, err := riscvWantIntReg(mnem, "rd", ops[2]) + if err != nil { + return nil, err + } + if rs1 < 0 { + return nil, fmt.Errorf("%s: expected integer register in rs1 position", mnem) } word = riscvAMOType(enc, rd, rs1, rs2) @@ -1710,47 +1778,65 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv // below carries the constant each operation reads (CLZ counts leading // zeros with an empty field, REV8 works on bytes at position 24). case len(ops) == 2 && isZbUnaryInstr(mnem): - rs1 := regFromOperand(ops[0]) - rd := regFromOperand(ops[1]) - if rd < 0 || rs1 < 0 { - return nil, fmt.Errorf("invalid register in %s", mnem) + rs1, err := riscvWantIntReg(mnem, "rs1", ops[0]) + if err != nil { + return nil, err + } + rd, err := riscvWantIntReg(mnem, "rd", ops[1]) + if err != nil { + return nil, err } word = riscvRType(enc, rd, rs1, riscvZbUnaryRS2[mnem]) // FP arithmetic: Go reverses the ISA order, writing rs2, rs1, rd. case len(ops) == 3 && isFPArithInstr(mnem): - rs2 := regFromOperand(ops[0]) - rs1 := regFromOperand(ops[1]) - rd := regFromOperand(ops[2]) - if rd < 0 || rs1 < 0 || rs2 < 0 { - return nil, fmt.Errorf("invalid FP register in %s", mnem) + rs2, err := riscvWantFloatReg(mnem, "rs2", ops[0]) + if err != nil { + return nil, err + } + rs1, err := riscvWantFloatReg(mnem, "rs1", ops[1]) + if err != nil { + return nil, err + } + rd, err := riscvWantFloatReg(mnem, "rd", ops[2]) + if err != nil { + return nil, err } word = riscvRType(enc, rd, rs1, rs2) // FP arithmetic (2-operand): FSQRT src, dst. case len(ops) == 2 && isFPArithInstr(mnem): - rs1 := regFromOperand(ops[0]) - rd := regFromOperand(ops[1]) - if rd < 0 || rs1 < 0 { - return nil, fmt.Errorf("invalid FP register in %s", mnem) + rs1, err := riscvWantFloatReg(mnem, "rs1", ops[0]) + if err != nil { + return nil, err + } + rd, err := riscvWantFloatReg(mnem, "rd", ops[1]) + if err != nil { + return nil, err } word = riscvRType(enc, rd, rs1, 0) // FP loads: INSTR addr, freg (Plan 9: source first). case len(ops) == 2 && isFPLoadInstr(mnem): - rd := regFromOperand(ops[1]) + rd, err := riscvWantFloatReg(mnem, "rd", ops[1]) + if err != nil { + return nil, err + } rs1, imm := memFromOperandWithFrame(ops[0], fi) - if rd < 0 || rs1 < 0 { - return nil, fmt.Errorf("invalid operand in %s", mnem) + if rs1 < 0 { + return nil, fmt.Errorf("%s: expected integer register in rs1 position", mnem) } word = riscvIType(enc, rd, rs1, imm) // FP stores: INSTR freg, addr (Plan 9: source first). case len(ops) == 2 && isFPStoreInstr(mnem): - rs2 := regFromOperand(ops[0]) + rs2, err := riscvWantFloatReg(mnem, "rs2", ops[0]) + if err != nil { + return nil, err + } rs1, imm := memFromOperandWithFrame(ops[1], fi) - if rs2 < 0 || rs1 < 0 { - return nil, fmt.Errorf("invalid operand in %s", mnem) + if rs1 < 0 { + return nil, fmt.Errorf("%s: expected integer register in rs1 position", mnem) } word = riscvSType(enc, rs1, rs2, imm) @@ -1759,29 +1845,44 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv // operand and the destination from the second whatever their parens. case len(ops) == 2 && isLRInstr(mnem): rs1 := regFromOperand(ops[0]) - rd := regFromOperand(ops[1]) - if rd < 0 || rs1 < 0 { - return nil, fmt.Errorf("invalid operand in %s", mnem) + rd, err := riscvWantIntReg(mnem, "rd", ops[1]) + if err != nil { + return nil, err + } + if rs1 < 0 { + return nil, fmt.Errorf("%s: expected integer register in rs1 position", mnem) } word = riscvAMOType(enc, rd, rs1, 0) // rs2=0 for LR // SC (store-conditional): INSTR src, (addr), dst, 3 operands. case len(ops) == 3 && isSCInstr(mnem): - rs2 := regFromOperand(ops[0]) + rs2, err := riscvWantIntReg(mnem, "rs2", ops[0]) + if err != nil { + return nil, err + } rs1, _ := memFromOperandWithFrame(ops[1], fi) - rd := regFromOperand(ops[2]) - if rd < 0 || rs1 < 0 || rs2 < 0 { - return nil, fmt.Errorf("invalid operand in %s", mnem) + rd, err := riscvWantIntReg(mnem, "rd", ops[2]) + if err != nil { + return nil, err + } + if rs1 < 0 { + return nil, fmt.Errorf("%s: expected integer register in rs1 position", mnem) } word = riscvAMOType(enc, rd, rs1, rs2) // FP compare: Go reverses the ISA order, writing rs2, rs1, rd. case len(ops) == 3 && isFPCmpInstr(mnem): - rs2 := regFromOperand(ops[0]) - rs1 := regFromOperand(ops[1]) - rd := regFromOperand(ops[2]) - if rd < 0 || rs1 < 0 || rs2 < 0 { - return nil, fmt.Errorf("invalid operand in %s", mnem) + rs2, err := riscvWantFloatReg(mnem, "rs2", ops[0]) + if err != nil { + return nil, err + } + rs1, err := riscvWantFloatReg(mnem, "rs1", ops[1]) + if err != nil { + return nil, err + } + rd, err := riscvWantIntReg(mnem, "rd", ops[2]) + if err != nil { + return nil, err } word = riscvRType(enc, rd, rs1, rs2) @@ -1792,10 +1893,13 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv if err != nil { return nil, err } - rs1 := regFromOperand(ops[1]) // source register - rd := regFromOperand(ops[2]) // destination - if rd < 0 || rs1 < 0 { - return nil, fmt.Errorf("invalid register in %s", mnem) + rs1, err := riscvWantIntReg(mnem, "rs1", ops[1]) // source register + if err != nil { + return nil, err + } + rd, err := riscvWantIntReg(mnem, "rd", ops[2]) // destination + if err != nil { + return nil, err } return encodeRISCVItypeImmediate(mnem, enc, rd, rs1, imm) @@ -1804,35 +1908,56 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv if err != nil { return nil, err } - rd := regFromOperand(ops[1]) - if rd < 0 { - return nil, fmt.Errorf("invalid register in %s", mnem) + rd, err := riscvWantIntReg(mnem, "rd", ops[1]) + if err != nil { + return nil, err } return encodeRISCVItypeImmediate(mnem, enc, rd, rd, imm) // Loads: rd, offset(rs1), Plan 9 order is LD src, dst. case len(ops) == 2 && isLoadInstr(mnem): - rd := regFromOperand(ops[1]) // destination (last operand) + rd, err := riscvWantIntReg(mnem, "rd", ops[1]) // destination (last operand) + if err != nil { + return nil, err + } rs1, imm := memFromOperandWithFrame(ops[0], fi) // memory source (first operand) - if rd < 0 || rs1 < 0 { - return nil, fmt.Errorf("invalid operand in %s", mnem) + if rs1 < 0 { + return nil, fmt.Errorf("%s: expected integer register in rs1 position", mnem) } word = riscvIType(enc, rd, rs1, imm) // Stores: Plan 9 order is SD src, dst (src=register, dst=memory). case len(ops) == 2 && isStoreInstr(mnem): - rs2 := regFromOperand(ops[0]) // source register (first operand) + rs2, err := riscvWantIntReg(mnem, "rs2", ops[0]) // source register (first operand) + if err != nil { + return nil, err + } rs1, imm := memFromOperandWithFrame(ops[1], fi) // memory dest (last operand) - if rs2 < 0 || rs1 < 0 { - return nil, fmt.Errorf("invalid operand in %s", mnem) + if rs1 < 0 { + return nil, fmt.Errorf("%s: expected integer register in rs1 position", mnem) + } + if off := int64(ops[1].Addr.Offset); ops[1].Addr.Sym == nil && (off < math.MinInt32 || off > math.MaxInt32) { + return nil, fmt.Errorf("%s: constant %d too large", mnem, off) } word = riscvSType(enc, rs1, rs2, imm) // Branches: rs1, rs2, label. BGT/BLE/BGTU/BLEU are the swapped-spelling // forms of BLT/BGE/BLTU/BGEU (bgt rs1, rs2 is blt rs2, rs1). case len(ops) == 3 && isBranchInstr(mnem): - rs1 := regFromOperand(ops[0]) - rs2 := regFromOperand(ops[1]) + rs1, err := riscvWantIntReg(mnem, "rs1", ops[0]) + if err != nil { + return nil, err + } + rs2, err := riscvWantIntReg(mnem, "rs2", ops[1]) + if err != nil { + return nil, err + } + // The toolchain rejects a branch whose third operand does not name + // a destination at all: a constant or a register-indirect address + // is not one (the N(PC) relative form is). + if ops[2].Imm.HasVal || (ops[2].Addr.Base != "" && ops[2].Addr.Base != "PC" && ops[2].Addr.Sym == nil) { + return nil, fmt.Errorf("%s: instruction with branch-like opcode lacks destination", mnem) + } target := labelFromOperand(ops[2]) switch mnem { case "BGT": @@ -1858,9 +1983,6 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv } } offset := int32(targetOff - pc) - if rs1 < 0 || rs2 < 0 { - return nil, fmt.Errorf("invalid register in %s", mnem) - } if err := riscvCheckBranchOffset(target, offset); err != nil { return nil, err } @@ -1875,13 +1997,16 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv case len(ops) == 2 && isUTypeInstr(mnem): var rd int var imm int32 + var err error if isImmOperand(ops[0]) { - imm, rd = immFromOperand(ops[0]), regFromOperand(ops[1]) + imm = immFromOperand(ops[0]) + rd, err = riscvWantIntReg(mnem, "rd", ops[1]) } else { - rd, imm = regFromOperand(ops[0]), immFromOperand(ops[1]) + rd, err = riscvWantIntReg(mnem, "rd", ops[0]) + imm = immFromOperand(ops[1]) } - if rd < 0 { - return nil, fmt.Errorf("invalid register in %s", mnem) + if err != nil { + return nil, err } if imm < -(1<<19) || imm > (1<<19)-1 { return nil, fmt.Errorf("%s: signed immediate 0x%x must be in range [-0x80000, 0x7ffff] (20 bits)", mnem, imm) @@ -1931,15 +2056,52 @@ func isImmOperand(op *ast.Operand) bool { func encodeRISCVMov(instr *ast.Instr, fi riscvFrameInfo, relocs *[]Reloc, lits *riscvLiterals, tlsSyms map[string]bool) ([]byte, error) { ops := instr.Operands if len(ops) != 2 { - return nil, fmt.Errorf("MOV expects 2 operands, got %d", len(ops)) + return nil, fmt.Errorf("illegal MOV instruction") } src := ops[0] dst := ops[1] + mnem := strings.ToUpper(instr.Mnemonic.Text) // Immediate → register. if isImmOperand(src) { - mnem := strings.ToUpper(instr.Mnemonic.Text) + // An address constant names a pseudo register, either as a symbol + // ($sym(SB), $sym+4(FP)) or as a bare offset whose raw spelling + // carries it ($8(SP)). The toolchain wants a register target for + // an address and supports the width-less MOV alone. + addrPseudo := "" + if src.Imm.Sym != nil && src.Imm.Sym.Pseudo != "" { + addrPseudo = src.Imm.Sym.Pseudo + } else if src.Imm.HasVal { + // The raw spelling carries the pseudo the number rides ($8(SP)); + // the spaces the parser records between the tokens come out. + raw := strings.Join(strings.Fields(src.Raw), "") + for _, p := range []string{"(FP)", "(SP)", "(SB)", "(PC)"} { + if strings.HasSuffix(raw, p) { + addrPseudo = strings.Trim(p, "()") + break + } + } + } + if addrPseudo != "" { + if isMemOperand(dst) { + return nil, fmt.Errorf("%s: address load must target register", mnem) + } + if mnem != "MOV" { + return nil, fmt.Errorf("%s: unsupported address load", mnem) + } + } else { + // A constant load: the toolchain wants a register target and + // supports only the width-less MOV spelling, plus the + // FP-constant forms of MOVF and MOVD. + fpConst := (mnem == "MOVF" || mnem == "MOVD") && !src.Imm.HasVal && src.Imm.Sym == nil && src.Imm.Str == "" + if isMemOperand(dst) { + return nil, fmt.Errorf("%s: constant load must target register", mnem) + } + if mnem != "MOV" && !fpConst { + return nil, fmt.Errorf("%s: unsupported constant load", mnem) + } + } // FP constant → FP register: a zero bit pattern moves through FMV // from X0, anything else loads from the pooled $f32/$f64 constant // symbol the toolchain synthesises (AUIPC + FLW/FLD through TMP). @@ -1978,22 +2140,37 @@ func encodeRISCVMov(instr *ast.Instr, fi riscvFrameInfo, relocs *[]Reloc, lits * } // MOV $sym(SB), rd, load address of a static symbol or external. if src.Imm.Sym != nil && src.Imm.Sym.Pseudo == "SB" { - rd := regFromOperand(dst) - if rd < 0 { - return nil, fmt.Errorf("MOV $sym(SB): invalid destination register") + rd, err := riscvWantMovReg(mnem, "rd", dst) + if err != nil { + return nil, err } return encodeRISCVSBAddr(src.Imm.Sym, rd, relocs), nil } // MOV $sym+off(FP|SP), rd: the address of a frame slot as an // immediate is the frame-adjusted offset against the hardware SP, // the toolchain's ADDI $adj, SP, rd (argframe+0(FP) in the runtime's - // reflect trampolines is the spelling). - if src.Imm.Sym != nil && (src.Imm.Sym.Pseudo == "FP" || src.Imm.Sym.Pseudo == "SP") { - rd := regFromOperand(dst) - if rd < 0 { - return nil, fmt.Errorf("MOV $%s(%s): invalid destination register", src.Imm.Sym.Name, src.Imm.Sym.Pseudo) + // reflect trampolines is the spelling). A bare offset with the + // pseudo in its raw spelling ($8(SP)) carries no frame adjustment. + if addrPseudo == "FP" || addrPseudo == "SP" { + rd, err := riscvWantMovReg(mnem, "rd", dst) + if err != nil { + return nil, err + } + off := int32(0) + if src.Imm.Sym != nil { + _, off = riscvResolvePseudo(src.Imm.Sym, fi) + } else { + v := src.Imm.Val + if src.Imm.Neg { + v = -v + } + off = int32(v) + if addrPseudo == "FP" { + off += int32(fi.autosize) + 8 + } else { + off += int32(fi.autosize) + } } - _, off := riscvResolvePseudo(src.Imm.Sym, fi) return riscvSPAddiBytes(rd, off), nil } // MOV $sym(FP/SP), rd, not supported: immediate symbol references @@ -2002,9 +2179,9 @@ func encodeRISCVMov(instr *ast.Instr, fi riscvFrameInfo, relocs *[]Reloc, lits * if src.Imm.Sym != nil && src.Imm.Sym.Pseudo != "" { return nil, fmt.Errorf("MOV $%s(%s): unsupported immediate symbol reference (only SB is supported)", src.Imm.Sym.Name, src.Imm.Sym.Pseudo) } - rd := regFromOperand(dst) - if rd < 0 { - return nil, fmt.Errorf("MOV $imm: invalid destination register") + rd, err := riscvWantMovReg(mnem, "rd", dst) + if err != nil { + return nil, err } imm := riscvOperandImm64(src) if int64(int32(imm)) != imm { @@ -2040,23 +2217,28 @@ func encodeRISCVMov(instr *ast.Instr, fi riscvFrameInfo, relocs *[]Reloc, lits * // Register → memory (store). if !isMemOperand(src) && isMemOperand(dst) { - rs2 := regFromOperand(src) + // The zero-extending widths synthesise their sign correction after + // a load; no store form exists, exactly as the toolchain rejects. + if mnem == "MOVBU" || mnem == "MOVHU" || mnem == "MOVWU" { + return nil, fmt.Errorf("%s: unsupported unsigned store", mnem) + } + rs2, err := riscvWantMovReg(mnem, "rs2", src) + if err != nil { + return nil, err + } // MOV rd, sym(SB), store to static data. A TLSBSS symbol takes // the local-exec sequence with the store through TMP. if dst.Addr.Sym != nil && dst.Addr.Sym.Pseudo == "SB" { - if rs2 < 0 { - return nil, fmt.Errorf("MOV rd, sym(SB): invalid source register") - } if tlsSyms[dst.Addr.Sym.Name] { - return riscvTLSBytes(riscvMovEnc(strings.ToUpper(instr.Mnemonic.Text), true), true, rs2, dst.Addr.Sym, relocs), nil + return riscvTLSBytes(riscvMovEnc(mnem, true), true, rs2, dst.Addr.Sym, relocs), nil } return encodeRISCVSBStore(dst.Addr.Sym, rs2, relocs), nil } rs1, off := memFromOperandWithFrame(dst, fi) - if rs2 < 0 || rs1 < 0 { - return nil, fmt.Errorf("MOV store: invalid operand") + if rs1 < 0 { + return nil, fmt.Errorf("%s: expected integer register in rs1 position", mnem) } - return riscvFrameMemOp(riscvMovEnc(strings.ToUpper(instr.Mnemonic.Text), true), true, rs2, rs1, off), nil + return riscvFrameMemOp(riscvMovEnc(mnem, true), true, rs2, rs1, off), nil } // Register → register. The width suffix selects the toolchain's @@ -3408,14 +3590,29 @@ func encodeRISCVExtended(mnem string, instr *ast.Instr, pc int, offsets map[stri case "FNES", "FNED": // INSTR fs1, fs2, xrd: the not-equal pseudos read as FEQ.S/FEQ.D // followed by XORI $1 on the result register (AFNES), two words. + // The toolchain's preprocess always reads three operands: two leave + // the FEQ source slot empty, and a memory destination is refused + // before the validation even runs. + if len(ops) == 2 { + if isMemOperand(ops[1]) { + return nil, true, fmt.Errorf("%s needs an integer register output", mnem) + } + return nil, true, fmt.Errorf("%s: expected float register in rs1 position", mnem) + } if len(ops) != 3 { return nil, true, fmt.Errorf("%s expects 3 operands, got %d", mnem, len(ops)) } - rs2 := regFromOperand(ops[0]) - rs1 := regFromOperand(ops[1]) - rd := regFromOperand(ops[2]) - if rs1 < 0 || rs2 < 0 || rd < 0 { - return nil, true, fmt.Errorf("%s: invalid register", mnem) + rs2, err := riscvWantFloatReg(mnem, "rs2", ops[0]) + if err != nil { + return nil, true, err + } + rs1, err := riscvWantFloatReg(mnem, "rs1", ops[1]) + if err != nil { + return nil, true, err + } + rd, err := riscvWantIntReg(mnem, "rd", ops[2]) + if err != nil { + return nil, true, err } feq := riscvEnc{0x53, 0x2, 0x50} // feq.s if mnem == "FNED" { @@ -3489,15 +3686,6 @@ func encodeRISCVCompressed(mnem string, instr *ast.Instr, pc int, offsets map[st } return op.Addr.Offset, true } - // regMem accepts a bare offset(rs) reference with a prime base register. - regMem := func(op *ast.Operand) (int, int64, bool) { - if op.Addr.Sym != nil || op.Addr.Base == "" { - return 0, 0, false - } - rs1 := riscvRegNum(op.Addr.Base) - return rs1, op.Addr.Offset, true - } - prime := func(r int) bool { return r >= 8 && r <= 15 } branchTarget := func(op *ast.Operand) (int, error) { if op.Addr.Sym == nil && op.Addr.Base == "PC" { n := int(op.Addr.Offset) @@ -3531,9 +3719,18 @@ func encodeRISCVCompressed(mnem string, instr *ast.Instr, pc int, offsets map[st if !ok { return nil, fmt.Errorf("%s: rs2 must be SP/X2", mnem) } - rd := regFromOperand(ops[1]) - if rd < 0 { - return nil, fmt.Errorf("%s: expected integer register in rd position", mnem) + var rd int + var err error + if mnem == "CFLDSP" { + rd, err = riscvWantFloatReg(mnem, "rd", ops[1]) + } else { + rd, err = riscvWantIntReg(mnem, "rd", ops[1]) + } + if err != nil { + return nil, err + } + if mnem != "CFLDSP" && rd == 0 { + return nil, fmt.Errorf("%s: cannot use register X0", mnem) } scale, hi := int64(4), int64(255) funct3 := uint32(0x2) @@ -3555,13 +3752,22 @@ func encodeRISCVCompressed(mnem string, instr *ast.Instr, pc int, offsets map[st if len(ops) != 2 { return nil, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops)) } - rs2 := regFromOperand(ops[0]) + var rs2 int + var err error + if mnem == "CFSDSP" { + rs2, err = riscvWantFloatReg(mnem, "rs2", ops[0]) + } else { + rs2, err = riscvWantIntReg(mnem, "rs2", ops[0]) + } + if err != nil { + return nil, err + } off, ok := stackMem(ops[1]) if !ok { return nil, fmt.Errorf("%s: rd must be SP/X2", mnem) } - if rs2 < 0 { - return nil, fmt.Errorf("%s: expected integer register in rs2 position", mnem) + if mnem != "CFSDSP" && rs2 == 0 { + return nil, fmt.Errorf("%s: cannot use register X0", mnem) } scale, hi, funct3 := int64(4), int64(255), uint32(0x6) if mnem != "CSWSP" { @@ -3583,13 +3789,19 @@ func encodeRISCVCompressed(mnem string, instr *ast.Instr, pc int, offsets map[st if len(ops) != 2 { return nil, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops)) } - rs1, off, ok := regMem(ops[0]) - if !ok || !prime(rs1) { - return nil, fmt.Errorf("%s: expected integer prime register in rs1 position", mnem) + rs1, err := riscvWantBaseReg(mnem, "rs1", "integer prime", ops[0], riscvBankInt, 8, 15) + if err != nil { + return nil, err } - rd := regFromOperand(ops[1]) - if rd < 0 || !prime(rd) { - return nil, fmt.Errorf("%s: expected integer prime register in rd position", mnem) + off := ops[0].Addr.Offset + var rd int + if mnem == "CFLD" { + rd, err = riscvWantFloatPrimeReg(mnem, "rd", ops[1]) + } else { + rd, err = riscvWantIntPrimeReg(mnem, "rd", ops[1]) + } + if err != nil { + return nil, err } scale, hi, funct3 := int64(4), int64(127), uint32(0x2) if mnem != "CLW" { @@ -3610,14 +3822,21 @@ func encodeRISCVCompressed(mnem string, instr *ast.Instr, pc int, offsets map[st if len(ops) != 2 { return nil, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops)) } - rs2 := regFromOperand(ops[0]) - rs1, off, ok := regMem(ops[1]) - if !ok || !prime(rs1) { - return nil, fmt.Errorf("%s: expected integer prime register in rs1 position", mnem) + var rs2 int + var err error + if mnem == "CFSD" { + rs2, err = riscvWantFloatPrimeReg(mnem, "rs2", ops[0]) + } else { + rs2, err = riscvWantIntPrimeReg(mnem, "rs2", ops[0]) } - if rs2 < 0 || !prime(rs2) { - return nil, fmt.Errorf("%s: expected integer prime register in rs2 position", mnem) + if err != nil { + return nil, err } + rs1, err := riscvWantBaseReg(mnem, "rs1", "integer prime", ops[1], riscvBankInt, 8, 15) + if err != nil { + return nil, err + } + off := ops[1].Addr.Offset scale, hi, funct3 := int64(4), int64(127), uint32(0x6) if mnem != "CSW" { scale, hi, funct3 = 8, 255, 0x7 @@ -3643,9 +3862,9 @@ func encodeRISCVCompressed(mnem string, instr *ast.Instr, pc int, offsets map[st } rs1 := 0 if mnem != "CJ" { - rs1 = regFromOperand(ops[0]) - if rs1 < 0 || !prime(rs1) { - return nil, fmt.Errorf("%s: expected integer prime register in rs1", mnem) + var err error + if rs1, err = riscvWantIntPrimeReg(mnem, "rs1", ops[0]); err != nil { + return nil, err } } off, err := branchTarget(ops[len(ops)-1]) @@ -3669,12 +3888,21 @@ func encodeRISCVCompressed(mnem string, instr *ast.Instr, pc int, offsets map[st return word16(rvcCB(funct3, uint32(rvcReg3(rs1)), int32(off))), nil case mnem == "CJR" || mnem == "CJALR": + if len(ops) == 2 { + pos := "rs2" + if mnem == "CJALR" { + pos = "rd" + } + if err := riscvWantNoReg(mnem, pos, ops[1]); err != nil { + return nil, err + } + } if len(ops) != 1 { return nil, fmt.Errorf("%s expects 1 operand, got %d", mnem, len(ops)) } - rs1 := regFromOperand(ops[0]) - if rs1 < 0 { - return nil, fmt.Errorf("%s: expected integer register in rs1", mnem) + rs1, err := riscvWantIntReg(mnem, "rs1", ops[0]) + if err != nil { + return nil, err } if rs1 == 0 { return nil, fmt.Errorf("%s: cannot use register X0 in rs1", mnem) @@ -3697,9 +3925,9 @@ func encodeRISCVCompressed(mnem string, instr *ast.Instr, pc int, offsets map[st if imm < -32 || imm > 31 { return nil, fmt.Errorf("%s: immediate %d must be in range [-32, 31]", mnem, imm) } - rd := regFromOperand(ops[1]) - if rd < 0 { - return nil, fmt.Errorf("%s: expected integer register in rd position", mnem) + rd, err := riscvWantIntReg(mnem, "rd", ops[1]) + if err != nil { + return nil, err } if rd == 0 { return nil, fmt.Errorf("%s: cannot use register X0 in rd", mnem) @@ -3720,9 +3948,9 @@ func encodeRISCVCompressed(mnem string, instr *ast.Instr, pc int, offsets map[st if imm < -32 || imm > 31 { return nil, fmt.Errorf("%s: immediate %d must be in range [-32, 31]", mnem, imm) } - rd := regFromOperand(ops[1]) - if rd < 0 { - return nil, fmt.Errorf("%s: expected integer register in rd position", mnem) + rd, err := riscvWantIntReg(mnem, "rd", ops[1]) + if err != nil { + return nil, err } if rd == 0 { return nil, fmt.Errorf("%s: cannot use register X0 in rd", mnem) @@ -3748,15 +3976,15 @@ func encodeRISCVCompressed(mnem string, instr *ast.Instr, pc int, offsets map[st if (mnem == "CADD" || mnem == "CADDI") && imm == 0 { return nil, fmt.Errorf("%s: immediate cannot be zero", mnem) } - rd := regFromOperand(ops[1]) + rd, err := riscvWantIntReg(mnem, "rd", ops[1]) + if err != nil { + return nil, err + } if len(ops) == 3 { - if rd != regFromOperand(ops[2]) { + if rs1, err := riscvWantIntReg(mnem, "rs1", ops[2]); err != nil || rs1 != rd { return nil, fmt.Errorf("%s: rd must be the same as rs1", mnem) } } - if rd < 0 { - return nil, fmt.Errorf("%s: expected integer register in rd position", mnem) - } funct3 := uint32(0x0) if mnem == "CADDW" || mnem == "CADDIW" { funct3 = 0x1 @@ -3780,8 +4008,7 @@ func encodeRISCVCompressed(mnem string, instr *ast.Instr, pc int, offsets map[st if imm%16 != 0 { return nil, fmt.Errorf("%s: immediate %d must be a multiple of 16", mnem, imm) } - rd := regFromOperand(ops[1]) - if rd != 2 { + if n, b := riscvBankedRegNum(operandRegName(ops[1])); b != riscvBankInt || n != 2 { return nil, fmt.Errorf("%s: rd must be SP/X2", mnem) } return word16(rvcADDI16SP(2, int32(imm))), nil @@ -3803,12 +4030,12 @@ func encodeRISCVCompressed(mnem string, instr *ast.Instr, pc int, offsets map[st if imm%4 != 0 { return nil, fmt.Errorf("%s: immediate %d must be a multiple of 4", mnem, imm) } - if regFromOperand(ops[1]) != 2 { + if n, b := riscvBankedRegNum(operandRegName(ops[1])); b != riscvBankInt || n != 2 { return nil, fmt.Errorf("%s: SP/X2 must be in rs1", mnem) } - rd := regFromOperand(ops[2]) - if rd < 0 || !prime(rd) { - return nil, fmt.Errorf("%s: expected integer prime register in rd", mnem) + rd, err := riscvWantIntPrimeReg(mnem, "rd", ops[2]) + if err != nil { + return nil, err } return word16(rvcCIW(0x0, uint32(rvcReg3(rd)), uint32(imm))), nil @@ -3839,23 +4066,23 @@ func encodeRISCVCompressed(mnem string, instr *ast.Instr, pc int, offsets map[st return nil, fmt.Errorf("%s: immediate %d must be in range [0, 63]", mnem, imm) } } - rd := regFromOperand(ops[1]) + rd, err := riscvWantIntReg(mnem, "rd", ops[1]) + if err != nil { + return nil, err + } if len(ops) == 3 { - if rd != regFromOperand(ops[2]) { + if rs1, err := riscvWantIntReg(mnem, "rs1", ops[2]); err != nil || rs1 != rd { return nil, fmt.Errorf("%s: rd must be the same as rs1", mnem) } } - if rd < 0 { - return nil, fmt.Errorf("%s: expected integer register in rd position", mnem) - } if mnem == "CSLLI" { if rd == 0 { return nil, fmt.Errorf("%s: cannot use register X0 in rd", mnem) } return word16(rvcSLLI(uint32(rd), uint32(imm)&0x3F)), nil } - if !prime(rd) { - return nil, fmt.Errorf("%s: expected integer prime register in rd", mnem) + if rd < 8 || rd > 15 { + return nil, fmt.Errorf("%s: expected integer prime register in rd position but got non-integer prime register %s", mnem, operandRegName(ops[1])) } funct2 := uint32(0x0) switch mnem { @@ -3868,16 +4095,21 @@ func encodeRISCVCompressed(mnem string, instr *ast.Instr, pc int, offsets map[st // Compressed integer register-register operations: destination last. case mnem == "CMV": + if len(ops) == 3 { + if err := riscvWantNoReg(mnem, "rs1", ops[2]); err != nil { + return nil, err + } + } if len(ops) != 2 { return nil, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops)) } - rs2 := regFromOperand(ops[0]) - rd := regFromOperand(ops[1]) - if rs2 < 0 { - return nil, fmt.Errorf("%s: expected integer register in rs2", mnem) + rs2, err := riscvWantIntReg(mnem, "rs2", ops[0]) + if err != nil { + return nil, err } - if rd < 0 { - return nil, fmt.Errorf("%s: expected integer register in rd", mnem) + rd, err := riscvWantIntReg(mnem, "rd", ops[1]) + if err != nil { + return nil, err } if rs2 == 0 { return nil, fmt.Errorf("%s: cannot use register X0 in rs2", mnem) @@ -3891,19 +4123,19 @@ func encodeRISCVCompressed(mnem string, instr *ast.Instr, pc int, offsets map[st if len(ops) != 2 && len(ops) != 3 { return nil, fmt.Errorf("%s expects 2 or 3 operands, got %d", mnem, len(ops)) } - rs2 := regFromOperand(ops[0]) - rd := regFromOperand(ops[1]) + rs2, err := riscvWantIntReg(mnem, "rs2", ops[0]) + if err != nil { + return nil, err + } + rd, err := riscvWantIntReg(mnem, "rd", ops[1]) + if err != nil { + return nil, err + } if len(ops) == 3 { - if rd != regFromOperand(ops[2]) { + if rs1, err := riscvWantIntReg(mnem, "rs1", ops[2]); err != nil || rs1 != rd { return nil, fmt.Errorf("%s: rd must be the same as rs1", mnem) } } - if rs2 < 0 { - return nil, fmt.Errorf("%s: expected integer register in rs2", mnem) - } - if rd < 0 { - return nil, fmt.Errorf("%s: expected integer register in rd", mnem) - } if rs2 == 0 { return nil, fmt.Errorf("%s: cannot use register X0 in rs2", mnem) } @@ -3913,8 +4145,12 @@ func encodeRISCVCompressed(mnem string, instr *ast.Instr, pc int, offsets map[st if mnem == "CADD" { return word16(rvcCR(0x9, uint32(rd), uint32(rs2))), nil } - if !prime(rd) || !prime(rs2) { - return nil, fmt.Errorf("%s: expected integer prime registers", mnem) + // The CA forms carry both fields in the three-bit prime encoding. + if rs2 < 8 || rs2 > 15 { + return nil, fmt.Errorf("%s: expected integer prime register in rs2 position but got non-integer prime register %s", mnem, operandRegName(ops[0])) + } + if rd < 8 || rd > 15 { + return nil, fmt.Errorf("%s: expected integer prime register in rd position but got non-integer prime register %s", mnem, operandRegName(ops[1])) } funct6 := uint32(0x23) funct2 := uint32(0x0) @@ -3945,35 +4181,58 @@ func encodeRISCVCompressed(mnem string, instr *ast.Instr, pc int, offsets map[st if imm < -32 || imm > 31 { return nil, fmt.Errorf("%s: immediate %d must be in range [-32, 31]", mnem, imm) } - rd := regFromOperand(ops[1]) - if len(ops) == 3 && rd != regFromOperand(ops[2]) { - return nil, fmt.Errorf("%s: rd must be the same as rs1", mnem) + rd, err := riscvWantIntReg(mnem, "rd", ops[1]) + if err != nil { + return nil, err } - if rd < 0 { - return nil, fmt.Errorf("%s: expected integer register in rd position", mnem) + if len(ops) == 3 { + if rs1, err := riscvWantIntReg(mnem, "rs1", ops[2]); err != nil || rs1 != rd { + return nil, fmt.Errorf("%s: rd must be the same as rs1", mnem) + } } return word16(rvcCI(0x1, uint32(rd), uint32(imm)&0x3F)), nil } if len(ops) != 2 && len(ops) != 3 { return nil, fmt.Errorf("%s expects 2 or 3 operands, got %d", mnem, len(ops)) } - rs2 := regFromOperand(ops[0]) - rd := regFromOperand(ops[1]) - if len(ops) == 3 && rd != regFromOperand(ops[2]) { - return nil, fmt.Errorf("%s: rd must be the same as rs1", mnem) + rs2, err := riscvWantIntReg(mnem, "rs2", ops[0]) + if err != nil { + return nil, err } - if rs2 < 0 || rd < 0 || !prime(rs2) || !prime(rd) { - return nil, fmt.Errorf("%s: expected integer prime registers", mnem) + rd, err := riscvWantIntReg(mnem, "rd", ops[1]) + if err != nil { + return nil, err + } + if len(ops) == 3 { + if rs1, err := riscvWantIntReg(mnem, "rs1", ops[2]); err != nil || rs1 != rd { + return nil, fmt.Errorf("%s: rd must be the same as rs1", mnem) + } + } + if rs2 < 8 || rs2 > 15 { + return nil, fmt.Errorf("%s: expected integer prime register in rs2 position but got non-integer prime register %s", mnem, operandRegName(ops[0])) + } + if rd < 8 || rd > 15 { + return nil, fmt.Errorf("%s: expected integer prime register in rd position but got non-integer prime register %s", mnem, operandRegName(ops[1])) } return word16(rvcCA(0x27, 0x1, uint32(rvcReg3(rd)), uint32(rvcReg3(rs2)))), nil case mnem == "CNOP": + if len(ops) == 1 { + if err := riscvWantNoReg(mnem, "rs2", ops[0]); err != nil { + return nil, err + } + } if len(ops) != 0 { return nil, fmt.Errorf("%s expects no operands", mnem) } return word16(0x0001), nil case mnem == "CEBREAK": + if len(ops) == 1 { + if err := riscvWantNoReg(mnem, "rs2", ops[0]); err != nil { + return nil, err + } + } if len(ops) != 0 { return nil, fmt.Errorf("%s expects no operands", mnem) } @@ -4014,14 +4273,25 @@ func encodeRISCVVector(mnem string, ops []*ast.Operand) ([]byte, bool, error) { // 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) == 2 { + // The assembler binds rs1 to the third slot, so a two-operand + // VSETVL leaves rs1 unset: the toolchain's rs1 rejection. + return nil, true, fmt.Errorf("%s: expected integer register in rs1 position", mnem) + } 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) + rs2, err := riscvWantIntReg(mnem, "rs2", ops[0]) + if err != nil { + return nil, true, err + } + rs1, err := riscvWantIntReg(mnem, "rs1", ops[1]) + if err != nil { + return nil, true, err + } + rd, err := riscvWantIntReg(mnem, "rd", ops[2]) + if err != nil { + return nil, true, err } return wordLE(riscvRType(riscvEnc{0x57, 0x7, 0x40}, rd, rs1, rs2)), true, nil @@ -4034,7 +4304,7 @@ func encodeRISCVVector(mnem string, ops []*ast.Operand) ([]byte, bool, error) { if isImmOperand(ops[0]) { avl = int(immFromOperand(ops[0])) if avl < 0 || avl > 31 { - return nil, true, fmt.Errorf("%s: avl immediate out of range [0, 31]", mnem) + return nil, true, fmt.Errorf("%s: avl immediate %d must be in range [0, 31] (5 bits)", mnem, avl) } } else { avl = reg(ops[0]) @@ -4043,7 +4313,7 @@ func encodeRISCVVector(mnem string, ops []*ast.Operand) ([]byte, bool, error) { } } if mnem == "VSETIVLI" && !isImmOperand(ops[0]) { - return nil, true, fmt.Errorf("VSETIVLI expects an immediate avl") + return nil, true, fmt.Errorf("%s: expected immediate value", mnem) } vsew, err := riscvVTypeToken(operandRegName(ops[1]), "E", map[string]int{"8": 0, "16": 1, "32": 2, "64": 3}) if err != nil { @@ -4069,9 +4339,9 @@ func encodeRISCVVector(mnem string, ops []*ast.Operand) ([]byte, bool, error) { default: return nil, true, fmt.Errorf("%s: invalid mask policy %q (want MA or MU)", mnem, operandRegName(ops[4])) } - rd := reg(ops[5]) - if rd < 0 { - return nil, true, fmt.Errorf("%s: invalid destination register", mnem) + rd, err := riscvWantIntReg(mnem, "rd", ops[5]) + if err != nil { + return nil, true, err } // An immediate avl always encodes as vsetivli, even under the // VSETVLI spelling: the toolchain canonicalises the pair, and @@ -4096,34 +4366,62 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { 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) + // vecSrcSlot answers the register bank and field name the mnemonic's + // source suffix selects (VADDVV takes a vector in the vs1 field, VADDVX + // an integer in rs1, VADDVF a float in rs1) and whether that suffix + // names an immediate instead. + vecSrcSlot := func() (riscvRegBank, bool, string) { + s := mnem + // The trailing M of the mask-mandatory forms names the mask, not + // the source: VADCVVM sources a vector, VADCVXM an integer, + // VADCVIM an immediate, VFMERGEVFM a float. + if n := len(s); n > 2 && s[n-1] == 'M' && + (s[n-2] == 'V' || s[n-2] == 'X' || s[n-2] == 'I' || s[n-2] == 'F') { + s = s[:n-1] } - return r, nil + switch { + case strings.HasSuffix(s, "VI") || strings.HasSuffix(s, "I"): + return riscvBankNone, true, "" + case strings.HasSuffix(s, "VX") || strings.HasSuffix(s, "X"): + return riscvBankInt, false, "rs1" + case strings.HasSuffix(s, "VF") || strings.HasSuffix(s, "F"): + return riscvBankFloat, false, "rs1" + } + return riscvBankVec, false, "vs1" + } + // vecSrc reads the source operand against its suffix's bank and name. + vecSrc := func(o *ast.Operand) (int, error) { + bank, _, pos := vecSrcSlot() + return riscvWantRegDescr(mnem, pos, bank.String(), o, bank, 0, 31) } // vecImm reads the immediate the entry's form bounds: signed five bits - // [-16, 15], or unsigned [0, 31] for the shifts and slides. + // [-16, 15], or unsigned [0, 31] for the shifts and slides. The + // toolchain's own table labels four of the unsigned forms signed (the + // narrowing shifts and clips), and the parity catalogue pins that + // wording, so the label follows the mnemonic. 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 { + label := "unsigned" + switch mnem { + case "VSSRLVI", "VSSRAVI", "VNCLIPUWI", "VNCLIPWI": + label = "signed" + } if v < 0 || v > 31 { - return 0, fmt.Errorf("%s: immediate %d out of range [0, 31]", mnem, v) + return 0, fmt.Errorf("%s: %s immediate %d must be in range [0, 31] (5 bits)", mnem, label, v) } } else if v < -16 || v > 15 { - return 0, fmt.Errorf("%s: immediate %d out of range [-16, 15]", mnem, v) + return 0, fmt.Errorf("%s: signed immediate %d must be in range [-16, 15] (5 bits)", 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) + if n, b := riscvBankedRegNum(operandRegName(o)); b != riscvBankVec || n != 0 { + return fmt.Errorf("%s: invalid vector mask register", mnem) } return nil } @@ -4160,11 +4458,11 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { return nil, true, err } } - vs2, err := vecReg(ops[1], "vs2") + vs2, err := riscvWantVecReg(mnem, "vs2", ops[1]) if err != nil { return nil, true, err } - vd, err := vecReg(ops[len(ops)-1], "vd") + vd, err := riscvWantVecReg(mnem, "vd", ops[len(ops)-1]) if err != nil { return nil, true, err } @@ -4175,7 +4473,7 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { } } else { var vs1 int - if vs1, err = vecReg(ops[0], "vs1"); err != nil { + if vs1, err = vecSrc(ops[0]); err != nil { return nil, true, err } rs1Field = int32(vs1) @@ -4194,11 +4492,11 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { return nil, true, err } } - vs2, err := vecReg(ops[0], "vs2") + vs2, err := riscvWantVecReg(mnem, "vs2", ops[0]) if err != nil { return nil, true, err } - vd, err := vecReg(ops[len(ops)-1], "vd") + vd, err := riscvWantVecReg(mnem, "vd", ops[len(ops)-1]) if err != nil { return nil, true, err } @@ -4209,7 +4507,7 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { } } else { var vs1 int - if vs1, err = vecReg(ops[1], "vs1"); err != nil { + if vs1, err = vecSrc(ops[1]); err != nil { return nil, true, err } rs1Field = int32(vs1) @@ -4236,15 +4534,15 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { if err != nil { return nil, true, err } - vs2, err := vecReg(ops[0], "vs2") + vs2, err := riscvWantVecReg(mnem, "vs2", ops[0]) if err != nil { return nil, true, err } - vd, err := vecReg(ops[len(ops)-1], "vd") + vd, err := riscvWantVecReg(mnem, "vd", ops[len(ops)-1]) if err != nil { return nil, true, err } - vs1, err := vecReg(ops[1], "vs1") + vs1, err := vecSrc(ops[1]) if err != nil { return nil, true, err } @@ -4273,19 +4571,21 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { if err != nil { return nil, true, err } - imm, err := vecImm(ops[0]) - if err != nil { - return nil, true, err + // The swap validates the immediate after the decrement, exactly as + // the toolchain reports it: VMSLTVI $-16 fails on the -17 it would + // encode. + if !isImmOperand(ops[0]) { + return nil, true, fmt.Errorf("%s expects an immediate first operand", mnem) } - imm-- + imm := immFromOperand(ops[0]) - 1 if imm < -16 || imm > 15 { - return nil, true, fmt.Errorf("%s: immediate %d leaves [-16, 15] after the swap", mnem, imm+1) + return nil, true, fmt.Errorf("%s: signed immediate %d must be in range [-16, 15] (5 bits)", mnem, imm) } - vs2, err := vecReg(ops[1], "vs2") + vs2, err := riscvWantVecReg(mnem, "vs2", ops[1]) if err != nil { return nil, true, err } - vd, err := vecReg(ops[len(ops)-1], "vd") + vd, err := riscvWantVecReg(mnem, "vd", ops[len(ops)-1]) if err != nil { return nil, true, err } @@ -4297,7 +4597,13 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { case vecUNARY, vecM2I: // INSTR vs2 [, V0], vd: one vector source, the fixed rs1 field; the - // m2i members take the destination in the integer file. + // m2i members take the destination in the integer file. A fourth + // operand has no rs3 slot to fill. + if len(ops) > 3 { + if err := riscvWantNoReg(mnem, "rs3", ops[2]); err != nil { + return nil, true, err + } + } if len(ops) != 2 && len(ops) != 3 { return nil, true, fmt.Errorf("%s expects 2 or 3 operands, got %d", mnem, len(ops)) } @@ -4307,11 +4613,18 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { return nil, true, err } } - vs2, err := vecReg(ops[0], "vs2") + vs2, err := riscvWantVecReg(mnem, "vs2", ops[0]) if err != nil { return nil, true, err } - vd, err := vecReg(ops[len(ops)-1], "vd") + var vd int + if mnem == "VCPOPM" || mnem == "VFIRSTM" { + // The popcount and first-bit reads land in an integer register; + // the mask-step family writes a vector destination. + vd, err = riscvWantIntReg(mnem, "rd", ops[len(ops)-1]) + } else { + vd, err = riscvWantVecReg(mnem, "vd", ops[len(ops)-1]) + } if err != nil { return nil, true, err } @@ -4320,7 +4633,10 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { 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 { + if len(ops) < 2 { + return nil, true, fmt.Errorf("%s: expected vector register in vd position", mnem) + } + if len(ops) > 3 { return nil, true, fmt.Errorf("%s expects 2 or 3 operands, got %d", mnem, len(ops)) } masked := len(ops) == 3 @@ -4336,11 +4652,11 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { if err != nil { return nil, true, err } - vs2, err := vecReg(ops[0], "vs2") + vs2, err := riscvWantVecReg(mnem, "vs2", ops[0]) if err != nil { return nil, true, err } - vd, err := vecReg(ops[len(ops)-1], "vd") + vd, err := riscvWantVecReg(mnem, "vd", ops[len(ops)-1]) if err != nil { return nil, true, err } @@ -4352,7 +4668,10 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { case vecVNOT: // VNOTV reads as VXORVI with the all-ones immediate. - if len(ops) != 2 && len(ops) != 3 { + if len(ops) < 2 { + return nil, true, fmt.Errorf("%s: expected vector register in vd position", mnem) + } + if len(ops) > 3 { return nil, true, fmt.Errorf("%s expects 2 or 3 operands, got %d", mnem, len(ops)) } masked := len(ops) == 3 @@ -4365,11 +4684,11 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { if err != nil { return nil, true, err } - vs2, err := vecReg(ops[0], "vs2") + vs2, err := riscvWantVecReg(mnem, "vs2", ops[0]) if err != nil { return nil, true, err } - vd, err := vecReg(ops[len(ops)-1], "vd") + vd, err := riscvWantVecReg(mnem, "vd", ops[len(ops)-1]) if err != nil { return nil, true, err } @@ -4397,11 +4716,11 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { if err != nil { return nil, true, err } - vs2, err := vecReg(ops[0], "vs2") + vs2, err := riscvWantVecReg(mnem, "vs2", ops[0]) if err != nil { return nil, true, err } - vd, err := vecReg(ops[len(ops)-1], "vd") + vd, err := riscvWantVecReg(mnem, "vd", ops[len(ops)-1]) if err != nil { return nil, true, err } @@ -4412,16 +4731,17 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { 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. + // INSTR vs2|xs2, vd (vmv.v.v/vmv.v.x): the source in the rs1 field, + // V0 fixed in vs2, the vm bit from the table. The suffix picks the + // source bank: VMVVV wants a vector, VMVVX an integer. if len(ops) != 2 { - return nil, true, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops)) + return nil, true, fmt.Errorf("%s: too many operands for instruction", mnem) } - vs2, err := vecReg(ops[0], "vs2") + vs2, err := vecSrc(ops[0]) if err != nil { return nil, true, err } - vd, err := vecReg(ops[1], "vd") + vd, err := riscvWantVecReg(mnem, "vd", ops[1]) if err != nil { return nil, true, err } @@ -4431,13 +4751,13 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { // 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)) + return nil, true, fmt.Errorf("%s: too many operands for instruction", mnem) } imm, err := vecImm(ops[0]) if err != nil { return nil, true, err } - vd, err := vecReg(ops[1], "vd") + vd, err := riscvWantVecReg(mnem, "vd", ops[1]) if err != nil { return nil, true, err } @@ -4448,11 +4768,11 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { if len(ops) != 2 { return nil, true, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops)) } - fs1, err := vecReg(ops[0], "fs1") + fs1, err := riscvWantFloatReg(mnem, "rs1", ops[0]) if err != nil { return nil, true, err } - vd, err := vecReg(ops[1], "vd") + vd, err := riscvWantVecReg(mnem, "vd", ops[1]) if err != nil { return nil, true, err } @@ -4461,15 +4781,24 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { case vecTWO: // INSTR vs2, vd: two-operand forms with the fixed rs1 field (the // extensions and conversions, the whole-register moves, the scalar - // reads). + // reads). The scalar reads turn the destination around: VMVXS + // reads a vector into an integer register, VFMVFS into a float. if len(ops) != 2 { return nil, true, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops)) } - vs2, err := vecReg(ops[0], "vs2") + vs2, err := riscvWantVecReg(mnem, "vs2", ops[0]) if err != nil { return nil, true, err } - vd, err := vecReg(ops[1], "vd") + var vd int + switch mnem { + case "VMVXS": + vd, err = riscvWantIntReg(mnem, "rd", ops[1]) + case "VFMVFS": + vd, err = riscvWantFloatReg(mnem, "rd", ops[1]) + default: + vd, err = riscvWantVecReg(mnem, "vd", ops[1]) + } if err != nil { return nil, true, err } @@ -4477,15 +4806,21 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { case vecTWOX: // INSTR xs1|fs1, vd: two-operand forms with the fixed vs2 field - // (vmv.s.x and vfmv.s.f). + // (vmv.s.x and vfmv.s.f). The suffix picks the scalar's bank and + // the toolchain's field name for it: rs2 in both spellings. if len(ops) != 2 { return nil, true, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops)) } - rs1, err := vecReg(ops[0], "rs1") + vd, err := riscvWantVecReg(mnem, "vd", ops[1]) if err != nil { return nil, true, err } - vd, err := vecReg(ops[1], "vd") + var rs1 int + if mnem == "VFMVSF" { + rs1, err = riscvWantFloatReg(mnem, "rs2", ops[0]) + } else { + rs1, err = riscvWantIntReg(mnem, "rs2", ops[0]) + } if err != nil { return nil, true, err } @@ -4495,16 +4830,16 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { // 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)) + return nil, true, fmt.Errorf("%s: invalid vector mask register", mnem) } if err := vecMask(ops[2]); err != nil { return nil, true, err } - vs2, err := vecReg(ops[1], "vs2") + vs2, err := riscvWantVecReg(mnem, "vs2", ops[1]) if err != nil { return nil, true, err } - vd, err := vecReg(ops[3], "vd") + vd, err := riscvWantVecReg(mnem, "vd", ops[3]) if err != nil { return nil, true, err } @@ -4518,7 +4853,7 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { } } else { var vs1 int - if vs1, err = vecReg(ops[0], "vs1"); err != nil { + if vs1, err = vecSrc(ops[0]); err != nil { return nil, true, err } rs1Field = int32(vs1) @@ -4529,16 +4864,16 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { // 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)) + return nil, true, fmt.Errorf("%s: invalid vector mask register", mnem) } if err := vecMask(ops[2]); err != nil { return nil, true, err } - vs2, err := vecReg(ops[1], "vs2") + vs2, err := riscvWantVecReg(mnem, "vs2", ops[1]) if err != nil { return nil, true, err } - vd, err := vecReg(ops[3], "vd") + vd, err := riscvWantVecReg(mnem, "vd", ops[3]) if err != nil { return nil, true, err } @@ -4549,7 +4884,7 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { } } else { var vs1 int - if vs1, err = vecReg(ops[0], "vs1"); err != nil { + if vs1, err = vecSrc(ops[0]); err != nil { return nil, true, err } rs1Field = int32(vs1) @@ -4558,15 +4893,21 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { case vecVMADC: // INSTR vs1|$imm, vs2, vd: the carry-producing forms; the third - // operand names the destination and may be V0. + // operand names the destination and may be V0. A fourth operand + // has no rs3 slot to fill, exactly as the toolchain writes it. + if len(ops) > 3 { + if err := riscvWantNoReg(mnem, "rs3", ops[3]); err != nil { + return nil, true, err + } + } if len(ops) != 3 { return nil, true, fmt.Errorf("%s expects 3 operands, got %d", mnem, len(ops)) } - vs2, err := vecReg(ops[1], "vs2") + vs2, err := riscvWantVecReg(mnem, "vs2", ops[1]) if err != nil { return nil, true, err } - vd, err := vecReg(ops[2], "vd") + vd, err := riscvWantVecReg(mnem, "vd", ops[2]) if err != nil { return nil, true, err } @@ -4577,7 +4918,7 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { } } else { var vs1 int - if vs1, err = vecReg(ops[0], "vs1"); err != nil { + if vs1, err = vecSrc(ops[0]); err != nil { return nil, true, err } rs1Field = int32(vs1) @@ -4592,17 +4933,17 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { 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") + vs1, err := vecSrc(ops[0]) if err != nil { return nil, true, err } vs2 := vs1 if !folded { - if vs2, err = vecReg(ops[1], "vs2"); err != nil { + if vs2, err = riscvWantVecReg(mnem, "vs2", ops[1]); err != nil { return nil, true, err } } - vd, err := vecReg(ops[len(ops)-1], "vd") + vd, err := riscvWantVecReg(mnem, "vd", ops[len(ops)-1]) if err != nil { return nil, true, err } @@ -4620,7 +4961,7 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { if err != nil { return nil, true, err } - r, err := vecReg(ops[0], "vd") + r, err := riscvWantVecReg(mnem, "vd", ops[0]) if err != nil { return nil, true, err } @@ -4637,7 +4978,7 @@ func encodeRISCVVecOp(mnem string, ops []*ast.Operand) ([]byte, bool, error) { return nil, true, err } } - vd, err := vecReg(ops[len(ops)-1], "vd") + vd, err := riscvWantVecReg(mnem, "vd", ops[len(ops)-1]) if err != nil { return nil, true, err } @@ -4852,46 +5193,81 @@ func encodeRISCVVecLS(mnem string, ops []*ast.Operand) ([]byte, bool, error) { 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) + // The base register is an integer file register; the unit and + // whole-register stores name its field rd, every other form rs1. + basePos := "rs1" + if !v.load && !strided && !indexed { + basePos = "rd" } - 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) + if ops[memIdx].Addr.Sym != nil && ops[memIdx].Addr.Sym.Pseudo != "" { + return nil, true, fmt.Errorf("%s: expected integer register in %s position", mnem, basePos) + } + rs1, err := riscvWantBaseReg(mnem, basePos, "integer", ops[memIdx], riscvBankInt, 0, 31) + if err != nil { + return nil, true, err + } + kind := "vd" + if !v.load && !strided && !indexed { + // The unit and whole-register stores name the data register vs1; + // loads and the stride and index families name it vd. + kind = "vs1" + } + vd, err := riscvWantVecReg(mnem, kind, ops[regIdx]) + if err != nil { + return nil, true, err } 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") { + if whole { + // The whole-register forms take no stride, index or mask. + if len(ops) != 2 { + return nil, true, fmt.Errorf("%s: too many operands for instruction", mnem) + } + } else { + rs2Filled := false + for _, mid := range ops[min(memIdx, regIdx)+1 : max(memIdx, regIdx)] { + name := operandRegName(mid) + n, b := riscvBankedRegNum(name) + // The unit forms have no rs2 slot: a middle operand is the + // mask, and V0 is the only one the encoding carries. + if !strided && !indexed { + if b != riscvBankVec || n != 0 { + return nil, true, fmt.Errorf("%s: invalid vector mask register", mnem) + } + masked = true + continue + } + // The strided and indexed forms fill the rs2 field first: the + // stride wants an integer register, the index a vector + // register. A vector beyond it is the mask, V0 alone. + if !rs2Filled { + if rs2 != v.rs2f { + return nil, true, fmt.Errorf("%s: too many operands for instruction", mnem) + } + if strided { + if b != riscvBankInt { + return nil, true, fmt.Errorf("%s: expected integer register in rs2 position but got non-integer register %s", mnem, name) + } + } else { + if b != riscvBankVec { + return nil, true, fmt.Errorf("%s: expected vector register in vs2 position but got non-vector register %s", mnem, name) + } + } + rs2 = n + rs2Filled = true + continue + } + if b != riscvBankVec || n != 0 { + return nil, true, fmt.Errorf("%s: invalid vector mask register", mnem) + } 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") { + if strided && !rs2Filled { 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) + if indexed && !rs2Filled { + return nil, true, fmt.Errorf("%s: expected vector register in vs2 position", 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 @@ -5064,6 +5440,99 @@ func regFromOperand(op *ast.Operand) int { return -1 } +// riscvWantIntReg reads one integer-bank register operand, rejecting a +// floating-point or vector register in its place. +func riscvWantIntReg(mnem, pos string, op *ast.Operand) (int, error) { + return riscvWantRegDescr(mnem, pos, "integer", op, riscvBankInt, 0, 31) +} + +// riscvWantFloatReg reads one floating-point register operand. +func riscvWantFloatReg(mnem, pos string, op *ast.Operand) (int, error) { + return riscvWantRegDescr(mnem, pos, "float", op, riscvBankFloat, 0, 31) +} + +// riscvWantVecReg reads one vector register operand. +func riscvWantVecReg(mnem, pos string, op *ast.Operand) (int, error) { + return riscvWantRegDescr(mnem, pos, "vector", op, riscvBankVec, 0, 31) +} + +// riscvWantIntPrimeReg reads one integer register operand from the prime +// range X8-X15, the constraint the compressed instruction fields carry. +func riscvWantIntPrimeReg(mnem, pos string, op *ast.Operand) (int, error) { + return riscvWantRegDescr(mnem, pos, "integer prime", op, riscvBankInt, 8, 15) +} + +// riscvWantFloatPrimeReg reads one floating-point register operand from the +// prime range F8-F15. +func riscvWantFloatPrimeReg(mnem, pos string, op *ast.Operand) (int, error) { + return riscvWantRegDescr(mnem, pos, "float prime", op, riscvBankFloat, 8, 15) +} + +// riscvWantRegDescr reads one register operand against the toolchain's +// wantReg contract: the bank, the prime range and the message shape +// ("expected integer prime register in rd position but got non-integer +// prime register X5"). The suffix only appears when the operand names a +// register at all, exactly as the toolchain writes it. +func riscvWantRegDescr(mnem, pos, descr string, op *ast.Operand, bank riscvRegBank, lo, hi int) (int, error) { + if op.Addr.Base != "" { + // A memory operand in a register slot: the toolchain reads no + // register from it and writes the message without a suffix. + return 0, fmt.Errorf("%s: expected %s register in %s position", mnem, descr, pos) + } + n, b := riscvBankedRegNum(operandRegName(op)) + if n < 0 { + return 0, fmt.Errorf("%s: expected %s register in %s position", mnem, descr, pos) + } + if b != bank || n < lo || n > hi { + return 0, fmt.Errorf("%s: expected %s register in %s position but got non-%s register %s", + mnem, descr, pos, descr, operandRegName(op)) + } + return n, nil +} + +// riscvWantNoReg rejects a register operand where the toolchain takes none: +// "expected no register in rs2 but got register X5". +func riscvWantNoReg(mnem, pos string, op *ast.Operand) error { + if name := operandRegName(op); name != "" { + if n, _ := riscvBankedRegNum(name); n >= 0 { + return fmt.Errorf("%s: expected no register in %s but got register %s", mnem, pos, name) + } + } + return nil +} + +// riscvMovBank answers the register bank a MOV width suffix addresses: the +// FP widths read the float file, every integer width the integer file. +func riscvMovBank(mnem string) riscvRegBank { + if mnem == "MOVF" || mnem == "MOVD" { + return riscvBankFloat + } + return riscvBankInt +} + +// riscvWantMovReg reads one register operand for a MOV-family instruction, +// against the bank the width suffix selects. +func riscvWantMovReg(mnem, pos string, op *ast.Operand) (int, error) { + bank := riscvMovBank(mnem) + return riscvWantRegDescr(mnem, pos, bank.String(), op, bank, 0, 31) +} + +// riscvWantBaseReg reads the base register of a memory-shaped operand as a +// register of the given bank and range, the way the register-based +// compressed loads and stores validate the base field they share with the +// offset. +func riscvWantBaseReg(mnem, pos, descr string, op *ast.Operand, bank riscvRegBank, lo, hi int) (int, error) { + n, b := riscvBankedRegNum(op.Addr.Base) + if n < 0 { + return 0, fmt.Errorf("%s: expected %s register in %s position", mnem, descr, pos) + } + if b != bank || n < lo || n > hi { + return 0, fmt.Errorf("%s: expected %s register in %s position but got non-%s register %s", + mnem, descr, pos, descr, op.Addr.Base) + } + return n, nil +} + func immFromOperand(op *ast.Operand) int32 { if op.Imm.HasVal { v := op.Imm.Val diff --git a/asm/riscv_encode.go b/asm/riscv_encode.go index f7a7600..7ad5e76 100644 --- a/asm/riscv_encode.go +++ b/asm/riscv_encode.go @@ -3,153 +3,192 @@ package asm +import "strings" + // RISC-V register encoding: maps register names to their 5-bit numbers. // The Go assembler uses the standard RISC-V ABI naming. // riscvRegNum returns the 5-bit register number for a RISC-V register name. // Returns -1 if the register is not recognized. func riscvRegNum(name string) int { + n, _ := riscvBankedRegNum(name) + return n +} + +// riscvRegBank classifies a RISC-V register name by its bank: the integer +// file (X0-X31 with the ABI aliases), the floating-point file (F0-F31 with +// the FP aliases) and the vector file (V0-V31). The toolchain's validate +// stage checks every operand against its instruction's bank and rejects the +// wrong one; the bank is what makes "F1" and "X1" different operands even +// though both encode as the number 1. +type riscvRegBank uint8 + +const ( + riscvBankNone riscvRegBank = iota // not a register name + riscvBankInt // X0-X31, ZERO, RA, SP, ... + riscvBankFloat // F0-F31, FT0-FT11, FS0-FS11, FA0-FA7 + riscvBankVec // V0-V31 +) + +// String names the bank the way the bank-mismatch diagnostics spell it. +func (b riscvRegBank) String() string { + switch b { + case riscvBankInt: + return "integer" + case riscvBankFloat: + return "float" + case riscvBankVec: + return "vector" + } + return "none" +} + +// riscvBankedRegNum returns the 5-bit register number for a name together +// with the bank the name belongs to. riscvBankNone with a negative number +// names no register at all. +func riscvBankedRegNum(name string) (int, riscvRegBank) { + switch name { + // Floating-point registers (F0-F31). + case "F0", "FT0": + return 0, riscvBankFloat + case "F1", "FT1": + return 1, riscvBankFloat + case "F2", "FT2": + return 2, riscvBankFloat + case "F3", "FT3": + return 3, riscvBankFloat + case "F4", "FT4": + return 4, riscvBankFloat + case "F5", "FT5": + return 5, riscvBankFloat + case "F6", "FT6": + return 6, riscvBankFloat + case "F7", "FT7": + return 7, riscvBankFloat + case "F8", "FS0": + return 8, riscvBankFloat + case "F9", "FS1": + return 9, riscvBankFloat + case "F10", "FA0": + return 10, riscvBankFloat + case "F11", "FA1": + return 11, riscvBankFloat + case "F12", "FA2": + return 12, riscvBankFloat + case "F13", "FA3": + return 13, riscvBankFloat + case "F14", "FA4": + return 14, riscvBankFloat + case "F15", "FA5": + return 15, riscvBankFloat + case "F16", "FA6": + return 16, riscvBankFloat + case "F17", "FA7": + return 17, riscvBankFloat + case "F18", "FS2": + return 18, riscvBankFloat + case "F19", "FS3": + return 19, riscvBankFloat + case "F20", "FS4": + return 20, riscvBankFloat + case "F21", "FS5": + return 21, riscvBankFloat + case "F22", "FS6": + return 22, riscvBankFloat + case "F23", "FS7": + return 23, riscvBankFloat + case "F24", "FS8": + return 24, riscvBankFloat + case "F25", "FS9": + return 25, riscvBankFloat + case "F26", "FS10": + return 26, riscvBankFloat + case "F27", "FS11": + return 27, riscvBankFloat + case "F28", "FT8": + return 28, riscvBankFloat + case "F29", "FT9": + return 29, riscvBankFloat + case "F30", "FT10": + return 30, riscvBankFloat + case "F31", "FT11": + return 31, riscvBankFloat + } + // Vector registers V0-V31 (the "V" extension). They share the + // register numbering with the integer file: a bare number 0-31. + if len(name) >= 2 && name[0] == 'V' { + if n, ok := parseRegDigits(name[1:], 31); ok { + return n, riscvBankVec + } + } switch name { // Numbered integer registers. case "X0", "ZERO": - return 0 + return 0, riscvBankInt case "X1", "RA", "LR": - return 1 + return 1, riscvBankInt case "X2", "SP": - return 2 + return 2, riscvBankInt case "X3", "GP": - return 3 + return 3, riscvBankInt case "X4", "TP": - return 4 + return 4, riscvBankInt case "X5", "T0": - return 5 + return 5, riscvBankInt case "X6", "T1": - return 6 + return 6, riscvBankInt case "X7", "T2": - return 7 + return 7, riscvBankInt case "X8", "S0", "FP": - return 8 + return 8, riscvBankInt case "X9", "S1": - return 9 + return 9, riscvBankInt case "X10", "A0": - return 10 + return 10, riscvBankInt case "X11", "A1": - return 11 + return 11, riscvBankInt case "X12", "A2": - return 12 + return 12, riscvBankInt case "X13", "A3": - return 13 + return 13, riscvBankInt case "X14", "A4": - return 14 + return 14, riscvBankInt case "X15", "A5": - return 15 + return 15, riscvBankInt case "X16", "A6": - return 16 + return 16, riscvBankInt case "X17", "A7": - return 17 + return 17, riscvBankInt case "X18", "S2": - return 18 + return 18, riscvBankInt case "X19", "S3": - return 19 + return 19, riscvBankInt case "X20", "S4": - return 20 + return 20, riscvBankInt case "X21", "S5": - return 21 + return 21, riscvBankInt case "X22", "S6": - return 22 + return 22, riscvBankInt case "X23", "S7": - return 23 + return 23, riscvBankInt case "X24", "S8": - return 24 + return 24, riscvBankInt case "X25", "S9": - return 25 + return 25, riscvBankInt case "X26", "S10", "CTXT": - return 26 + return 26, riscvBankInt case "X27", "S11", "g": - return 27 + return 27, riscvBankInt case "X28", "T3": - return 28 + return 28, riscvBankInt case "X29", "T4": - return 29 + return 29, riscvBankInt case "X30", "T5": - return 30 + return 30, riscvBankInt case "X31", "T6", "TMP": - return 31 - // Floating-point registers (F0-F31). - case "F0", "FT0": - return 0 - case "F1", "FT1": - return 1 - case "F2", "FT2": - return 2 - case "F3", "FT3": - return 3 - case "F4", "FT4": - return 4 - case "F5", "FT5": - return 5 - case "F6", "FT6": - return 6 - case "F7", "FT7": - return 7 - case "F8", "FS0": - return 8 - case "F9", "FS1": - return 9 - case "F10", "FA0": - return 10 - case "F11", "FA1": - return 11 - case "F12", "FA2": - return 12 - case "F13", "FA3": - return 13 - case "F14", "FA4": - return 14 - case "F15", "FA5": - return 15 - case "F16", "FA6": - return 16 - case "F17", "FA7": - return 17 - case "F18", "FS2": - return 18 - case "F19", "FS3": - return 19 - case "F20", "FS4": - return 20 - case "F21", "FS5": - return 21 - case "F22", "FS6": - return 22 - case "F23", "FS7": - return 23 - case "F24", "FS8": - return 24 - case "F25", "FS9": - return 25 - case "F26", "FS10": - return 26 - case "F27", "FS11": - return 27 - case "F28", "FT8": - return 28 - case "F29", "FT9": - return 29 - case "F30", "FT10": - return 30 - case "F31", "FT11": - return 31 - default: - // Vector registers V0-V31 (the "V" extension). They share the - // register numbering with the integer file: a bare number 0-31. - if len(name) >= 2 && name[0] == 'V' { - if n, ok := parseRegDigits(name[1:], 31); ok { - return n - } - } - return -1 + return 31, riscvBankInt } + return -1, riscvBankNone } // parseRegDigits parses a decimal register suffix and reports whether it is @@ -472,6 +511,56 @@ var riscvCvtTable = map[string]riscvCvtEnc{ "FMVSX": {0x78, 0x0, 0x0, 0x53}, } +// riscvCvtBanks answers which register bank each operand of an FP +// conversion or move carries, read from the mnemonic's direction: FCVTWS +// moves float32 into an integer register (rd integer, rs1 float), FCVTSW +// the reverse, FCLASS reads a float into an integer mask and the FMV +// spellings follow their X position. The rd bank comes first. +func riscvCvtBanks(mnem string) (riscvRegBank, riscvRegBank) { + // The classify and bit-move forms. + switch { + case strings.HasPrefix(mnem, "FCLASS"): + return riscvBankInt, riscvBankFloat + case strings.HasPrefix(mnem, "FMV"): + if len(mnem) > 3 && mnem[3] == 'X' { + return riscvBankInt, riscvBankFloat // FMVXD, FMVXW, FMVXS + } + return riscvBankFloat, riscvBankInt // FMVDX, FMVWX, FMVFS, FMVSX + } + if !strings.HasPrefix(mnem, "FCVT") || len(mnem) < 6 { + return riscvBankNone, riscvBankNone + } + rest := mnem[4:] + // intWU/LU + floatSDQ: float → integer. + switch rest[:2] { + case "WU", "LU": + if isRiscvWidthLetter(rest[2]) { + return riscvBankInt, riscvBankFloat + } + } + switch rest[0] { + case 'W', 'L': + if isRiscvWidthLetter(rest[1]) { + return riscvBankInt, riscvBankFloat + } + } + // floatSDQ + intWU/LU: integer → float. + if isRiscvWidthLetter(rest[0]) { + switch rest[1:] { + case "W", "WU", "L", "LU": + return riscvBankFloat, riscvBankInt + } + // float → float width conversion. + return riscvBankFloat, riscvBankFloat + } + return riscvBankNone, riscvBankNone +} + +// isRiscvWidthLetter reports whether c names a float width (S, D or Q). +func isRiscvWidthLetter(c byte) bool { + return c == 'S' || c == 'D' || c == 'Q' +} + // riscvCvtType encodes an FP conversion instruction. // Layout: funct7 | rs2(convtype) | rs1 | funct3(rm) | rd | opcode. func riscvCvtType(enc riscvCvtEnc, rd, rs1 int) uint32 { diff --git a/asm/riscv_toolchain_test.go b/asm/riscv_toolchain_test.go index faa8cf1..5f9f85c 100644 --- a/asm/riscv_toolchain_test.go +++ b/asm/riscv_toolchain_test.go @@ -209,12 +209,12 @@ func TestRISCVToolchainRejections(t *testing.T) { { name: "fence predecessor", src: "\tFENCE X, W\n", - want: "FENCE: invalid predecessor operand", + want: "invalid FENCE predecessor operand", }, { name: "fence successor", src: "\tFENCE W, Q\n", - want: "FENCE: invalid successor operand", + want: "invalid FENCE successor operand", }, { name: "immediate CSR pseudo with register",