feat(asm): add RVC compression for branches, arithmetic, and FP
Assisted-by: DeepSeek V4 Pro
This commit is contained in:
+77
-12
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user