fix(asm): compress the riscv64 two-operand arithmetic and immediate tail
Assisted-by: GLM 5.3
This commit is contained in:
1 parent
f0318d2c99
commit
da35883633
2 files changed
+66
-22
No files matched your search
+65
-21
@@ -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
@@ -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 {
|
||||
|
||||
Reference in new issue
Block a user