feat(asm): add RVC compression for branches, arithmetic, and FP

Assisted-by: DeepSeek V4 Pro
This commit is contained in:
2026-08-03 01:08:00 +02:00
parent 243b087116
commit 7721353d44
4 changed files with 237 additions and 20 deletions
+77 -12
View File
@@ -136,7 +136,7 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
word = riscvIType(riscvEnc{0x13, 0x0, 0x00}, 0, 0, 0)
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil
case "JMP":
// JMP = JAL X0, target
// JMP = JAL X0, target. Try C.J compression.
var target string
if len(ops) >= 1 {
target = labelFromOperand(ops[0])
@@ -146,6 +146,11 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
return nil, fmt.Errorf("undefined label %q", target)
}
offset := int32(targetOff - pc)
// C.J: funct3=0x5, offset in ±2 KB, bit 0 must be 0.
if offset >= -2048 && offset <= 2046 && offset%2 == 0 {
c16 := rvcCJ(0x5, offset)
return []byte{byte(c16), byte(c16 >> 8)}, nil
}
word = riscvJType(0, offset)
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil
case "JAL":
@@ -162,6 +167,11 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
return nil, fmt.Errorf("undefined label %q", target)
}
offset := int32(targetOff - pc)
// JAL X0, target → C.J when offset fits.
if rd == 0 && offset >= -2048 && offset <= 2046 && offset%2 == 0 {
c16 := rvcCJ(0x5, offset)
return []byte{byte(c16), byte(c16 >> 8)}, nil
}
word = riscvJType(rd, offset)
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil
@@ -370,12 +380,23 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
if !ok {
return nil, fmt.Errorf("undefined label %q", target)
}
rs1Off := 0 // placeholder
_ = rs1Off
offset := int32(targetOff - pc)
if rs1 < 0 || rs2 < 0 {
return nil, fmt.Errorf("invalid register in %s", mnem)
}
// Try C.BEQZ / C.BNEZ compression.
if (mnem == "BEQ" || mnem == "BNE") && rs2 == 0 && isRVCIntReg(rs1) {
if cOff := offset; cOff >= -256 && cOff <= 254 && cOff%2 == 0 {
funct3 := uint32(0x6) // C.BEQZ
if mnem == "BNE" {
funct3 = 0x7 // C.BNEZ
}
c16 := rvcCB(funct3, rvcReg3(rs1), offset)
return []byte{byte(c16), byte(c16 >> 8)}, nil
}
}
word = riscvBType(enc, rs1, rs2, offset)
// U-type: rd, imm.
@@ -575,30 +596,74 @@ func tryCompressRVC(instr *ast.Instr, fi riscvFrameInfo) (uint16, bool) {
}
case "JMP":
// C.J — but offset is computed at encode time.
// C.J — handled in encodeRISCVInstr with actual offset.
return 0, false
case "BEQ":
// BEQ rs, ZERO, target → C.BEQZ when offset fits in ±256.
// C.BEQZ — handled in encodeRISCVInstr with actual offset.
return 0, false
case "BNE":
// C.BNEZ — handled in encodeRISCVInstr with actual offset.
return 0, false
case "ADD":
// ADD rd, rs2 → C.ADD when rd == rs1 and both in prime regs (rd ≠ 0).
// ADD is commutative: if rd == rs2, swap.
if len(ops) == 3 {
rs1 := regFromOperand(ops[0])
rs2 := regFromOperand(ops[1])
if rs1 != -1 && rs2 == 0 && isRVCIntReg(rs1) {
// Could be C.BEQZ but offset computed at encode time.
return 0, false
rd := regFromOperand(ops[2])
if rd != -1 && rs1 != -1 && rs2 != -1 && rd != 0 {
if rd == rs1 && isRVCIntReg(rd) && isRVCIntReg(rs2) && rs2 != 0 {
// C.ADD: funct6=0x27, funct2=0x0 (CA-type)
return rvcCA(0x27, 0x0, rvcReg3(rd), rvcReg3(rs2)), true
}
if rd == rs2 && isRVCIntReg(rd) && isRVCIntReg(rs1) && rs1 != 0 {
// Swap: C.ADD rd, rs1
return rvcCA(0x27, 0x0, rvcReg3(rd), rvcReg3(rs1)), true
}
}
}
case "BNE":
// BNE rs, ZERO, target → C.BNEZ when offset fits in ±256.
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
}
rs1 := regFromOperand(ops[0])
rs2 := regFromOperand(ops[1])
if rs1 != -1 && rs2 == 0 && isRVCIntReg(rs1) {
return 0, false
rd := regFromOperand(ops[2])
if rd != -1 && rs1 != -1 && rs2 != -1 && rd != 0 {
if rd == rs1 && isRVCIntReg(rd) && isRVCIntReg(rs2) && rs2 != 0 {
return rvcCA(0x23, funct2, rvcReg3(rd), rvcReg3(rs2)), true
}
}
}
case "FLD":
// FLD rd, imm(SP) → C.FLDSP (CI-type, funct3=0x1).
rd, rs1, imm := extractLDParams(instr, fi)
if rs1 == 2 && rd != -1 && imm >= 0 && imm < 512 && imm%8 == 0 {
return rvcCI(0x1, uint32(rd), uint32(imm)>>3), true
}
case "FSD":
// FSD rs2, imm(SP) → C.FSDSP (CSS-type, funct3=0x5).
rs2, rs1, imm := extractSDParams(instr, fi)
if rs1 == 2 && rs2 != -1 && imm >= 0 && imm < 512 && imm%8 == 0 {
return rvcCSS(0x5, uint32(rs2), uint32(imm)>>3), true
}
case "LUI":
// LUI rd, imm → C.LUI when rd≠0, rd≠SP, imm nonzero and fits in 6 bits.
if len(ops) == 2 {