fix(asm): read the MOV family registers against their banks

Assisted-by: GLM 5.3 Flash
This commit is contained in:
petrbalvin committed 2026-10-07 19:40:32 +02:00
1 parent 4ff47fe17a
commit 86cb785e58
1 file changed
+53 -14
+53 -14
View File
@@ -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