fix(asm): compress the riscv64 two-operand arithmetic and immediate tail

Assisted-by: GLM 5.3
This commit is contained in:
petrbalvin committed 2026-10-07 13:51:02 +02:00
1 parent f0318d2c99
commit da35883633
2 files changed
+66 -22

No files matched your search

+65 -21
View File
@@ -400,7 +400,7 @@ func riscvInstrSize(instr *ast.Instr, fi riscvFrameInfo) int {
if isRVCIntReg(rd) && off > 0 && off < 1024 && off%4 == 0 {
return 2 // C.ADDI4SPN
}
return riscvItypeImmediateSize("ADDI", off)
return riscvItypeImmediateSize("ADDI", rd, 2, off)
}
// Frame-relative loads and stores: a frame offset beyond the signed
// 12-bit range materialises the address in X31 first.
@@ -417,7 +417,16 @@ func riscvInstrSize(instr *ast.Instr, fi riscvFrameInfo) int {
if immNeg {
imm = -imm
}
return riscvItypeImmediateSize(mnem, imm)
rd, rs1 := -1, -1
switch len(ops) {
case 3:
rs1 = regFromOperand(ops[1])
rd = regFromOperand(ops[2])
case 2:
rd = regFromOperand(ops[1])
rs1 = rd
}
return riscvItypeImmediateSize(mnem, rd, rs1, imm)
}
// BYTE lays down one raw byte per operand.
if mnem == "BYTE" {
@@ -2310,13 +2319,23 @@ func encodeRISCVItypeImmediate(mnem string, enc riscvEnc, rd, rs1 int, imm int32
if !ok {
return nil, fmt.Errorf("%s: unsupported operation %q", mnem, opMn)
}
// The toolchain's compress pass runs over the expansion's instructions,
// and the final ADD takes the C.ADD form whenever rd == rs1 (the two-
// operand ADDI spelling); the other ops carry TMP (X31) as rs2, which
// only C.ADD's full-width rs2 field can hold.
if opMn == "ADD" && rd == rs1 && rd != 0 {
c := rvcCR(0x9, uint32(rd), uint32(tmp))
out = append(out, byte(c), byte(c>>8))
return out, nil
}
out = append(out, wordLE(riscvRType(opEnc, rd, rs1, tmp))...)
return out, nil
}
// riscvItypeImmediateSize returns the encoded byte length of an I-type
// immediate instruction, accounting for the large-immediate expansion.
func riscvItypeImmediateSize(mnem string, imm int32) int {
// immediate instruction, accounting for the large-immediate expansion and
// the C.ADD the compress pass gives the expansion's final ADD when rd == rs1.
func riscvItypeImmediateSize(mnem string, rd, rs1 int, imm int32) int {
if imm >= -2048 && imm <= 2047 {
return 4
}
@@ -2329,7 +2348,12 @@ func riscvItypeImmediateSize(mnem string, imm int32) int {
return 8
}
low, high := splitRISCV32Imm(imm)
size := 4 // the R-type op (TMP is X31, never compressed)
// The R-type op: TMP is X31, whose full-width rs2 only C.ADD can hold,
// and only when rd == rs1 (the two-operand ADDI spelling).
size := 4
if mnem == "ADDI" && rd == rs1 && rd != 0 {
size = 2
}
if high != 0 && high >= -32 && high <= 31 {
size += 2 // C.LUI
} else {
@@ -2448,13 +2472,17 @@ func tryCompressRVC(instr *ast.Instr, fi riscvFrameInfo) (uint16, bool) {
if mnem == "MOV" && len(ops) == 2 && isImmOperand(ops[0]) {
return 0, false
}
// MOV reg, reg → C.MV (CR-type: funct4=0x8).
// MOV reg, reg → C.MV (CR-type: funct4=0x8); the X0 source is ADDI
// $0, X0, rd, which the toolchain's compress pass turns into C.LI $0.
if mnem == "MOV" && len(ops) == 2 && !isMemOperand(ops[0]) && !isMemOperand(ops[1]) && !isImmOperand(ops[0]) {
rs1 := regFromOperand(ops[0])
rd := regFromOperand(ops[1])
if rs1 != -1 && rd != -1 && rs1 != 0 && rd != 0 {
return rvcCR(0x8, uint32(rd), uint32(rs1)), true
}
if rs1 == 0 && rd > 0 {
return rvcCI(0x2, uint32(rd), 0), true
}
}
rd, rs1, imm := extractLDParams(instr, fi)
if rs1 == 2 && rd != 0 && rd != -1 && imm >= 0 && imm < 512 && imm%8 == 0 {
@@ -2553,6 +2581,14 @@ func tryCompressRVC(instr *ast.Instr, fi riscvFrameInfo) (uint16, bool) {
case "ADD":
// ADD rs2, rs1, rd → C.ADD (CR-type, funct4=0x9) when rd == rs1; ADD
// is commutative, so if rd == rs2, swap. ADD rs2, X0, rd is C.MV.
// The two-operand form ADD rs2, rd reads rd as rs1.
if len(ops) == 2 {
rs2 := regFromOperand(ops[0])
rd := regFromOperand(ops[1])
if rd != -1 && rs2 != -1 && rd != 0 && rs2 != 0 {
return rvcCR(0x9, uint32(rd), uint32(rs2)), true
}
}
if len(ops) == 3 {
rs2 := regFromOperand(ops[0])
rs1 := regFromOperand(ops[1])
@@ -2573,18 +2609,15 @@ func tryCompressRVC(instr *ast.Instr, fi riscvFrameInfo) (uint16, bool) {
case "SUB", "XOR", "OR", "AND":
// C.SUB (0x23,0), C.XOR (0x23,1), C.OR (0x23,2), C.AND (0x23,3)
if len(ops) == 3 {
var funct2 uint32
switch mnem {
case "SUB":
funct2 = 0x0
case "XOR":
funct2 = 0x1
case "OR":
funct2 = 0x2
case "AND":
funct2 = 0x3
funct2 := map[string]uint32{"SUB": 0x0, "XOR": 0x1, "OR": 0x2, "AND": 0x3}[mnem]
if len(ops) == 2 {
rs2 := regFromOperand(ops[0])
rd := regFromOperand(ops[1])
if rd != -1 && rs2 != -1 && isRVCIntReg(rd) && isRVCIntReg(rs2) && rs2 != 0 {
return rvcCA(0x23, funct2, rvcReg3(rd), rvcReg3(rs2)), true
}
}
if len(ops) == 3 {
rs2 := regFromOperand(ops[0])
rs1 := regFromOperand(ops[1])
rd := regFromOperand(ops[2])
@@ -2601,11 +2634,18 @@ func tryCompressRVC(instr *ast.Instr, fi riscvFrameInfo) (uint16, bool) {
case "ADDW", "SUBW":
// C.ADDW (0x27,1) / C.SUBW (0x27,0), CA-type, prime regs.
if len(ops) == 3 {
funct2 := uint32(0x0)
if mnem == "ADDW" {
funct2 = 0x1
funct2 := uint32(0x0)
if mnem == "ADDW" {
funct2 = 0x1
}
if len(ops) == 2 {
rs2 := regFromOperand(ops[0])
rd := regFromOperand(ops[1])
if rs2 != -1 && rd != -1 && isRVCIntReg(rd) && isRVCIntReg(rs2) {
return rvcCA(0x27, funct2, rvcReg3(rd), rvcReg3(rs2)), true
}
}
if len(ops) == 3 {
rs2 := regFromOperand(ops[0])
rs1 := regFromOperand(ops[1])
rd := regFromOperand(ops[2])
@@ -2655,6 +2695,10 @@ func tryCompressRVC(instr *ast.Instr, fi riscvFrameInfo) (uint16, bool) {
case "ADDIW":
rd, rs1, imm := extractITypeParams(instr)
if immNeg {
// SUBW $imm, rd arrives as ADDIW with the negated immediate.
imm = -imm
}
if rd == rs1 && rd != 0 && imm >= -32 && imm <= 31 {
return rvcCI(0x1, uint32(rd), uint32(imm)&0x3F), true
}
+1 -1
View File
@@ -344,7 +344,7 @@ func riscvGuard(fi riscvFrameInfo) ([]byte, Reloc, error) {
off := int32(fi.autosize - stackSmall)
mov := encodeRISCVLoadImm(7, off)
out = append(out, mov...)
addiLen := riscvItypeImmediateSize("ADDI", -off)
addiLen := riscvItypeImmediateSize("ADDI", 7, 2, -off)
out = append(out, wordLE(riscvBType(riscvEnc{0x63, 0x06, 0x00}, 2, 7, int32(addiLen+8)))...)
addi, err := encodeRISCVItypeImmediate("ADDI", riscvEnc{0x13, 0x0, 0x00}, 7, 2, -off)
if err != nil {