diff --git a/asm/riscv_assemble.go b/asm/riscv_assemble.go index 383033c..cdc42de 100644 --- a/asm/riscv_assemble.go +++ b/asm/riscv_assemble.go @@ -2195,24 +2195,27 @@ func encodeRISCVMov(instr *ast.Instr, fi riscvFrameInfo, relocs *[]Reloc, lits * // Memory → register (load). if isMemOperand(src) && !isMemOperand(dst) { - rd := regFromOperand(dst) + rd, err := riscvWantMovReg(mnem, "rd", dst) + if err != nil { + return nil, err + } // MOV sym(SB), rd, load from static data. A TLSBSS symbol takes // the local-exec sequence: LUI + ADDIW carry the offset against TP, // the ADD folds the thread pointer in, the access reads through TMP. if src.Addr.Sym != nil && src.Addr.Sym.Pseudo == "SB" { - if rd < 0 { - return nil, fmt.Errorf("MOV sym(SB): invalid destination register") - } if tlsSyms[src.Addr.Sym.Name] { - return riscvTLSBytes(riscvMovEnc(strings.ToUpper(instr.Mnemonic.Text), false), false, rd, src.Addr.Sym, relocs), nil + return riscvTLSBytes(riscvMovEnc(mnem, false), false, rd, src.Addr.Sym, relocs), nil } return encodeRISCVSBLoad(src.Addr.Sym, rd, relocs), nil } - rs1, off := memFromOperandWithFrame(src, fi) - if rd < 0 || rs1 < 0 { - return nil, fmt.Errorf("MOV load: invalid operand") + if err := riscvWantMemBase(mnem, "rs1", src); err != nil { + return nil, err } - return riscvFrameMemOp(riscvMovEnc(strings.ToUpper(instr.Mnemonic.Text), false), false, rd, rs1, off), nil + rs1, off := memFromOperandWithFrame(src, fi) + if rs1 < 0 { + return nil, fmt.Errorf("%s: expected integer register in rs1 position", mnem) + } + return riscvFrameMemOp(riscvMovEnc(mnem, false), false, rd, rs1, off), nil } // Register → memory (store). @@ -2234,6 +2237,9 @@ func encodeRISCVMov(instr *ast.Instr, fi riscvFrameInfo, relocs *[]Reloc, lits * } return encodeRISCVSBStore(dst.Addr.Sym, rs2, relocs), nil } + if err := riscvWantMemBase(mnem, "rs1", dst); err != nil { + return nil, err + } rs1, off := memFromOperandWithFrame(dst, fi) if rs1 < 0 { return nil, fmt.Errorf("%s: expected integer register in rs1 position", mnem) @@ -2248,13 +2254,25 @@ func encodeRISCVMov(instr *ast.Instr, fi riscvFrameInfo, relocs *[]Reloc, lits * // MOVWU zero-extend through SLLI+SRLI; bare MOV is ADDI $0, which the // RVC pass compresses. { + srcF, dstF := riscvIsFloatRegOperand(src), riscvIsFloatRegOperand(dst) + if mnem == "MOVF" || mnem == "MOVD" { + if !srcF && !dstF { + return nil, fmt.Errorf("%s: expected float register in rd position but got non-float register %s", + mnem, operandRegName(dst)) + } + } else { + // The integer widths move through the integer file alone: a + // float or vector register in either slot is the toolchain's + // bank rejection, the destination reported first. + if _, err := riscvWantMovReg(mnem, "rd", dst); err != nil { + return nil, err + } + if _, err := riscvWantMovReg(mnem, "rs1", src); err != nil { + return nil, err + } + } rs1 := regFromOperand(src) rd := regFromOperand(dst) - if rd < 0 || rs1 < 0 { - return nil, fmt.Errorf("MOV: invalid register operand") - } - mnem := strings.ToUpper(instr.Mnemonic.Text) - srcF, dstF := riscvIsFloatRegOperand(src), riscvIsFloatRegOperand(dst) switch mnem { case "MOVF", "MOVD": switch { @@ -2265,12 +2283,21 @@ func encodeRISCVMov(instr *ast.Instr, fi riscvFrameInfo, relocs *[]Reloc, lits * } return wordLE(op | uint32(rs1)<<15 | uint32(rs1)<<20 | uint32(rd)<<7), nil case dstF && !srcF: + // The FMV spellings move the bit pattern through the + // integer file: a vector register in the source slot + // names no integer register. + if _, err := riscvWantMovReg("MOV", "rs1", src); err != nil { + return nil, err + } op := uint32(0x78) << 25 // FMV.W.X if mnem == "MOVD" { op = uint32(0x79) << 25 // FMV.D.X } return wordLE(op | uint32(rs1)<<15 | uint32(rd)<<7 | 0x53), nil case srcF && !dstF: + if _, err := riscvWantMovReg("MOV", "rd", dst); err != nil { + return nil, err + } op := uint32(0x70) << 25 // FMV.X.W if mnem == "MOVD" { op = uint32(0x71) << 25 // FMV.X.D @@ -5533,6 +5560,18 @@ func riscvWantBaseReg(mnem, pos, descr string, op *ast.Operand, bank riscvRegBan return n, nil } +// riscvWantMemBase validates the base register of a memory operand that is +// not a frame reference: the addressing forms take integer registers only. +func riscvWantMemBase(mnem, pos string, op *ast.Operand) error { + if op.Addr.Base == "" || (op.Addr.Sym != nil && op.Addr.Sym.Pseudo != "") { + return nil // a frame-pseudo or symbol reference names no register + } + if _, b := riscvBankedRegNum(op.Addr.Base); b != riscvBankInt { + return fmt.Errorf("%s: expected integer register in %s position but got non-integer register %s", mnem, pos, op.Addr.Base) + } + return nil +} + func immFromOperand(op *ast.Operand) int32 { if op.Imm.HasVal { v := op.Imm.Val