fix(asm): correct RISC-V operand order and complete RVC compression
Assisted-by: DeepSeek V4 Pro
This commit is contained in:
+87
-37
@@ -324,26 +324,44 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
|
||||
}
|
||||
|
||||
switch {
|
||||
// R-type: Plan 9 order is INSTR src1, src2, dst (destination last).
|
||||
// R-type: Go reverses the ISA order, writing rs2, rs1, rd (destination
|
||||
// last); the two-operand form INSTR rs2, rd uses rd as rs1.
|
||||
case len(ops) == 3 && isRTypeInstr(mnem):
|
||||
rs1 := regFromOperand(ops[0]) // source 1 (first operand)
|
||||
rs2 := regFromOperand(ops[1]) // source 2 (second operand)
|
||||
rs2 := regFromOperand(ops[0]) // first operand = rs2
|
||||
rs1 := regFromOperand(ops[1]) // second operand = rs1
|
||||
rd := regFromOperand(ops[2]) // destination (last operand)
|
||||
if rd < 0 || rs1 < 0 || rs2 < 0 {
|
||||
return nil, fmt.Errorf("invalid register in %s", mnem)
|
||||
}
|
||||
word = riscvRType(enc, rd, rs1, rs2)
|
||||
|
||||
// I-type shift (SLLI, SRLI, SRAI): INSTR rs, $shamt, rd.
|
||||
case len(ops) == 2 && isRTypeInstr(mnem):
|
||||
rs2 := regFromOperand(ops[0]) // source (first operand)
|
||||
rd := regFromOperand(ops[1]) // destination (second operand)
|
||||
if rd < 0 || rs2 < 0 {
|
||||
return nil, fmt.Errorf("invalid register in %s", mnem)
|
||||
}
|
||||
word = riscvRType(enc, rd, rd, rs2)
|
||||
|
||||
// I-type shift (SLLI, SRLI, SRAI): INSTR $shamt, rs1, rd; the two-operand
|
||||
// form INSTR $shamt, rd uses rd as the source.
|
||||
case len(ops) == 3 && isShiftImmInstr(mnem):
|
||||
rs1 := regFromOperand(ops[0])
|
||||
shamt := int(immFromOperand(ops[1]))
|
||||
shamt := int(immFromOperand(ops[0]))
|
||||
rs1 := regFromOperand(ops[1])
|
||||
rd := regFromOperand(ops[2])
|
||||
if rd < 0 || rs1 < 0 {
|
||||
return nil, fmt.Errorf("invalid register in %s", mnem)
|
||||
}
|
||||
word = riscvRType(enc, rd, rs1, shamt)
|
||||
|
||||
case len(ops) == 2 && isShiftImmInstr(mnem):
|
||||
shamt := int(immFromOperand(ops[0]))
|
||||
rd := regFromOperand(ops[1])
|
||||
if rd < 0 {
|
||||
return nil, fmt.Errorf("invalid register in %s", mnem)
|
||||
}
|
||||
word = riscvRType(enc, rd, rd, shamt)
|
||||
|
||||
// AMO atomics: Plan 9 order is INSTR src, (addr), dst.
|
||||
case len(ops) == 3 && isAMOInstr(mnem):
|
||||
rs2 := regFromOperand(ops[0]) // source value
|
||||
@@ -354,10 +372,10 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
|
||||
}
|
||||
word = riscvAMOType(enc, rd, rs1, rs2)
|
||||
|
||||
// FP arithmetic: Plan 9 order is INSTR src1, src2, dst.
|
||||
// FP arithmetic: Go reverses the ISA order, writing rs2, rs1, rd.
|
||||
case len(ops) == 3 && isFPArithInstr(mnem):
|
||||
rs1 := regFromOperand(ops[0])
|
||||
rs2 := regFromOperand(ops[1])
|
||||
rs2 := regFromOperand(ops[0])
|
||||
rs1 := regFromOperand(ops[1])
|
||||
rd := regFromOperand(ops[2])
|
||||
if rd < 0 || rs1 < 0 || rs2 < 0 {
|
||||
return nil, fmt.Errorf("invalid FP register in %s", mnem)
|
||||
@@ -410,26 +428,35 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
|
||||
}
|
||||
word = riscvAMOType(enc, rd, rs1, rs2)
|
||||
|
||||
// FP compare: INSTR src1, src2, dst(int) — result in integer register.
|
||||
// FP compare: Go reverses the ISA order, writing rs2, rs1, rd.
|
||||
case len(ops) == 3 && isFPCmpInstr(mnem):
|
||||
rs1 := regFromOperand(ops[0])
|
||||
rs2 := regFromOperand(ops[1])
|
||||
rs2 := regFromOperand(ops[0])
|
||||
rs1 := regFromOperand(ops[1])
|
||||
rd := regFromOperand(ops[2])
|
||||
if rd < 0 || rs1 < 0 || rs2 < 0 {
|
||||
return nil, fmt.Errorf("invalid operand in %s", mnem)
|
||||
}
|
||||
word = riscvRType(enc, rd, rs1, rs2)
|
||||
|
||||
// I-type with immediate: Plan 9 order is INSTR src, imm, dst.
|
||||
// I-type with immediate: Plan 9 order is INSTR $imm, rs1, rd; the
|
||||
// two-operand form INSTR $imm, rd uses rd as the source.
|
||||
case len(ops) == 3 && isITypeInstr(mnem):
|
||||
rs1 := regFromOperand(ops[0]) // source register
|
||||
imm := immFromOperand(ops[1]) // immediate
|
||||
imm := immFromOperand(ops[0]) // immediate
|
||||
rs1 := regFromOperand(ops[1]) // source register
|
||||
rd := regFromOperand(ops[2]) // destination
|
||||
if rd < 0 || rs1 < 0 {
|
||||
return nil, fmt.Errorf("invalid register in %s", mnem)
|
||||
}
|
||||
word = riscvIType(enc, rd, rs1, imm)
|
||||
|
||||
case len(ops) == 2 && isITypeInstr(mnem):
|
||||
imm := immFromOperand(ops[0])
|
||||
rd := regFromOperand(ops[1])
|
||||
if rd < 0 {
|
||||
return nil, fmt.Errorf("invalid register in %s", mnem)
|
||||
}
|
||||
word = riscvIType(enc, rd, rd, imm)
|
||||
|
||||
// Loads: rd, offset(rs1) — Plan 9 order is LD src, dst.
|
||||
case len(ops) == 2 && isLoadInstr(mnem):
|
||||
rd := regFromOperand(ops[1]) // destination (last operand)
|
||||
@@ -742,6 +769,10 @@ func tryCompressRVC(instr *ast.Instr, fi riscvFrameInfo) (uint16, bool) {
|
||||
if rd == -1 || rs1 == -1 {
|
||||
return 0, false
|
||||
}
|
||||
if rd == 2 && rs1 == 2 && imm != 0 && imm%16 == 0 && imm >= -512 && imm <= 511 {
|
||||
// C.ADDI16SP: ADDI to SP by a nonzero 16-byte multiple.
|
||||
return rvcADDI16SP(2, imm), true
|
||||
}
|
||||
if rd == rs1 && rd != 0 && imm != 0 && imm >= -32 && imm <= 31 {
|
||||
// C.ADDI: funct3=0x0, rs1/rd, nzimm[5:0]
|
||||
return rvcCI(0x0, uint32(rd), uint32(imm)&0x3F), true
|
||||
@@ -754,6 +785,10 @@ func tryCompressRVC(instr *ast.Instr, fi riscvFrameInfo) (uint16, bool) {
|
||||
// C.MV: funct4=0x8, rd, rs1 (CR-type)
|
||||
return rvcCR(0x8, uint32(rd), uint32(rs1)), true
|
||||
}
|
||||
if rd == 0 && rs1 == 0 && imm == 0 {
|
||||
// C.NOP
|
||||
return 0x0001, true
|
||||
}
|
||||
|
||||
case "JAL":
|
||||
// JAL X0, target → C.J when offset fits in ±2KB.
|
||||
@@ -777,11 +812,11 @@ func tryCompressRVC(instr *ast.Instr, fi riscvFrameInfo) (uint16, bool) {
|
||||
return 0, false
|
||||
|
||||
case "ADD":
|
||||
// ADD rd, rs2 → C.ADD (CR-type, funct4=0x9) when rd == rs1; ADD is
|
||||
// commutative, so if rd == rs2, swap.
|
||||
// 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.
|
||||
if len(ops) == 3 {
|
||||
rs1 := regFromOperand(ops[0])
|
||||
rs2 := regFromOperand(ops[1])
|
||||
rs2 := regFromOperand(ops[0])
|
||||
rs1 := regFromOperand(ops[1])
|
||||
rd := regFromOperand(ops[2])
|
||||
if rd != -1 && rs1 != -1 && rs2 != -1 && rd != 0 {
|
||||
if rd == rs1 && rs2 != 0 {
|
||||
@@ -790,6 +825,10 @@ func tryCompressRVC(instr *ast.Instr, fi riscvFrameInfo) (uint16, bool) {
|
||||
if rd == rs2 && rs1 != 0 {
|
||||
return rvcCR(0x9, uint32(rd), uint32(rs1)), true
|
||||
}
|
||||
if rs1 == 0 && rs2 != 0 {
|
||||
// ADD rs2, X0, rd → C.MV rd, rs2.
|
||||
return rvcCR(0x8, uint32(rd), uint32(rs2)), true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -807,13 +846,17 @@ func tryCompressRVC(instr *ast.Instr, fi riscvFrameInfo) (uint16, bool) {
|
||||
case "AND":
|
||||
funct2 = 0x3
|
||||
}
|
||||
rs1 := regFromOperand(ops[0])
|
||||
rs2 := regFromOperand(ops[1])
|
||||
rs2 := regFromOperand(ops[0])
|
||||
rs1 := regFromOperand(ops[1])
|
||||
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
|
||||
}
|
||||
// AND/OR/XOR are commutative; SUB is not.
|
||||
if mnem != "SUB" && rd == rs2 && isRVCIntReg(rd) && isRVCIntReg(rs1) && rs1 != 0 {
|
||||
return rvcCA(0x23, funct2, rvcReg3(rd), rvcReg3(rs1)), true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -848,33 +891,32 @@ func tryCompressRVC(instr *ast.Instr, fi riscvFrameInfo) (uint16, bool) {
|
||||
}
|
||||
|
||||
case "SLLI", "SRLI", "SRAI":
|
||||
// C.SLLI (funct3=0x0), C.SRLI (funct3=0x4, funct2=0), C.SRAI (funct3=0x4, funct2=1).
|
||||
rd, rs1, imm := extractITypeParams(instr, fi)
|
||||
if rd == rs1 && rd != 0 && imm != 0 && imm >= 1 && imm <= 63 {
|
||||
if mnem == "SLLI" {
|
||||
// C.SLLI: funct3=0, CI-type with shamt in bits [12|6:2].
|
||||
// For simplicity, use the standard CI format — the shamt is in imm[5:0].
|
||||
return rvcCI(0x0, uint32(rd), uint32(imm)&0x3F), true
|
||||
// C.SLLI: funct3=0, op=10 quadrant, shamt in bits [12|6:2].
|
||||
return rvcSLLI(uint32(rd), uint32(imm)&0x3F), true
|
||||
}
|
||||
if isRVCIntReg(rd) {
|
||||
funct2 := uint32(0x0)
|
||||
if mnem == "SRAI" {
|
||||
funct2 = 0x1
|
||||
}
|
||||
// CB-format shift: funct3=0x4, shamt in bits [12|6:2].
|
||||
// Use simplified encoding for now.
|
||||
_ = funct2
|
||||
return rvcCI(0x0, uint32(rd), uint32(imm)&0x3F), true
|
||||
// C.SRLI/C.SRAI: CB-type, funct3=0x4.
|
||||
return rvcCBShift(funct2, rvcReg3(rd), uint32(imm)&0x3F), true
|
||||
}
|
||||
}
|
||||
|
||||
case "ANDI":
|
||||
rd, rs1, imm := extractITypeParams(instr, fi)
|
||||
if isRVCIntReg(rd) && rd == rs1 && imm >= -32 && imm <= 31 {
|
||||
// C.ANDI: funct3=0x4, funct2=0x2 (CB-type).
|
||||
// Simplified encoding for now.
|
||||
return rvcCI(0x0, uint32(rd), uint32(imm)&0x3F), true
|
||||
// C.ANDI: CB-type, funct3=0x4, funct2=0x2.
|
||||
return rvcCBShift(0x2, rvcReg3(rd), uint32(imm)&0x3F), true
|
||||
}
|
||||
|
||||
case "EBREAK":
|
||||
// C.EBREAK: CR-type, funct4=0x9, rd=0, rs2=0.
|
||||
return rvcCR(0x9, 0, 0), true
|
||||
}
|
||||
|
||||
return 0, false
|
||||
@@ -911,15 +953,23 @@ func extractSDParams(instr *ast.Instr, fi riscvFrameInfo) (rs2, rs1 int, imm int
|
||||
return
|
||||
}
|
||||
|
||||
// extractITypeParams extracts rd, rs1, and immediate for an I-type instruction.
|
||||
// extractITypeParams extracts rd, rs1, and immediate for an I-type
|
||||
// instruction. The Plan 9 order is INSTR $imm, rs1, rd (3 operands) or
|
||||
// INSTR $imm, rd (2 operands, rd is also the source).
|
||||
func extractITypeParams(instr *ast.Instr, fi riscvFrameInfo) (rd, rs1 int, imm int32) {
|
||||
ops := instr.Operands
|
||||
if len(ops) != 3 {
|
||||
switch len(ops) {
|
||||
case 3:
|
||||
imm = immFromOperand(ops[0])
|
||||
rs1 = regFromOperand(ops[1])
|
||||
rd = regFromOperand(ops[2])
|
||||
case 2:
|
||||
imm = immFromOperand(ops[0])
|
||||
rd = regFromOperand(ops[1])
|
||||
rs1 = rd
|
||||
default:
|
||||
return -1, -1, 0
|
||||
}
|
||||
rs1 = regFromOperand(ops[0])
|
||||
imm = immFromOperand(ops[1])
|
||||
rd = regFromOperand(ops[2])
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user