fix(asm): carry the riscv64 scalar pseudos and the swapped branches

Assisted-by: GLM 5.3 Flash
This commit is contained in:
petrbalvin committed 2026-10-07 00:47:27 +02:00
1 parent 82e8919208
commit 955bc6643e
2 files changed
+124 -46

No files matched your search

+91 -46
View File
@@ -437,7 +437,7 @@ func riscvExtendedSize(mnem string, ops []*ast.Operand) int {
case "NOP":
// The toolchain drops a bare NOP entirely.
return 0
case "ANDN", "ORN", "XNOR":
case "ANDN", "ORN", "XNOR", "FNES", "FNED":
return 8
case "MAX", "MAXU", "MIN", "MINU":
if riscvIdenticalMinMax(mnem, ops) {
@@ -894,7 +894,7 @@ func riscvPCRelOffset(instr *ast.Instr) (int, bool) {
if len(instr.Operands) != 1 && len(instr.Operands) != 2 {
return 0, false
}
case "BEQ", "BNE", "BLT", "BGE", "BLTU", "BGEU",
case "BEQ", "BNE", "BLT", "BGE", "BLTU", "BGEU", "BGT", "BLE", "BGTU", "BLEU",
"BEQZ", "BNEZ", "BLTZ", "BGEZ", "BLEZ", "BGTZ":
if len(instr.Operands) < 2 {
return 0, false
@@ -1138,16 +1138,22 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
word = riscvJType(0, offset)
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil
}
// JMP (X5): an indirect branch, the toolchain's JALR X0, 0(X5).
// JMP (X5) and JMP 4(X5): an indirect branch, the toolchain's
// JALR X0, imm(X5) with the offset carried in the I-type
// immediate (JMP 4(X5) is 0x67804200).
if ops[0].Addr.Sym == nil && ops[0].Addr.Base != "" {
if ops[0].Addr.Offset != 0 || ops[0].Addr.Index != "" {
if ops[0].Addr.Index != "" {
return nil, fmt.Errorf("JMP: invalid indirect operand %q", ops[0].Raw)
}
rs1 := riscvRegNum(ops[0].Addr.Base)
if rs1 < 0 {
return nil, fmt.Errorf("JMP: unknown branch register %q", ops[0].Addr.Base)
}
word = riscvIType(riscvEnc{0x67, 0x0, 0x00}, 0, rs1, 0)
imm := int32(ops[0].Addr.Offset)
if imm < -2048 || imm > 2047 {
return nil, fmt.Errorf("JMP: displacement %d does not fit in 12 bits", imm)
}
word = riscvIType(riscvEnc{0x67, 0x0, 0x00}, 0, rs1, imm)
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil
}
}
@@ -1301,6 +1307,47 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
}
word = riscvRType(riscvEnc{0x73, 0x0, 0x09}, 0, rs1, rs2)
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil
case "NEG", "NEGW":
// INSTR rs [, rd]: SUB/SUBW with X0 in the rs1 field, the
// one-operand form negating in place (ANEG: NEG rs, rd -> SUB rs,
// X0, rd). The toolchain pins the bytes: NEG X5 is 0x405002b3.
if len(ops) != 1 && len(ops) != 2 {
return nil, fmt.Errorf("%s expects 1 or 2 operands, got %d", mnem, len(ops))
}
rs := regFromOperand(ops[0])
rd := rs
if len(ops) == 2 {
rd = regFromOperand(ops[1])
}
if rs < 0 || rd < 0 {
return nil, fmt.Errorf("%s: invalid register operand", mnem)
}
enc := riscvEnc{0x33, 0x0, 0x20} // sub
if mnem == "NEGW" {
enc = riscvEnc{0x3B, 0x0, 0x20} // subw
}
word = riscvRType(enc, rd, 0, rs)
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil
case "SEQZ", "SNEZ":
// INSTR rs, rd: the set-equal and set-not-equal pseudos read as
// SLTIU $1 and SLTU against X0 (ASEQZ/ASNEZ). The toolchain pins
// the bytes: SEQZ X14, X15 is 0x00173793.
if len(ops) != 2 {
return nil, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops))
}
rs := regFromOperand(ops[0])
rd := regFromOperand(ops[1])
if rs < 0 || rd < 0 {
return nil, fmt.Errorf("%s: invalid register operand", mnem)
}
if mnem == "SEQZ" {
word = riscvIType(riscvEnc{0x13, 0x3, 0x00}, rd, rs, 1) // sltiu $1
} else {
word = riscvRType(riscvEnc{0x33, 0x3, 0x00}, rd, 0, rs) // sltu rd, x0, rs
}
return []byte{byte(word), byte(word >> 8), byte(word >> 16), byte(word >> 24)}, nil
}
// FP conversion / move instructions use a separate table (rs2 encodes
@@ -2767,7 +2814,9 @@ func encodeRISCVExtended(mnem string, instr *ast.Instr, pc int, offsets map[stri
}
return wordLE(riscvIType(riscvEnc{0x73, 0x2, 0x00}, rd, 0, 0xC01)), true, nil
case "NEG", "NOT", "SEQZ":
case "NOT":
// NEG and SEQZ have their own cases in encodeRISCVInstr's pseudo
// switch; NOT reads as XORI $-1.
if len(ops) != 1 && len(ops) != 2 {
return nil, true, fmt.Errorf("%s expects 1 or 2 operands, got %d", mnem, len(ops))
}
@@ -2781,12 +2830,8 @@ func encodeRISCVExtended(mnem string, instr *ast.Instr, pc int, offsets map[stri
}
var word uint32
switch mnem {
case "NEG":
word = riscvRType(riscvInstrTable["SUB"], rd, 0, rs)
case "NOT":
word = riscvIType(riscvInstrTable["XORI"], rd, rs, -1)
case "SEQZ":
word = riscvIType(riscvInstrTable["SLTIU"], rd, rs, 1)
}
return wordLE(word), true, nil
@@ -2975,49 +3020,49 @@ func encodeRISCVExtended(mnem string, instr *ast.Instr, pc int, offsets map[stri
out = append(out, wordLE(riscvRType(riscvInstrTable["OR"], rd, 31, rd))...)
return out, true, nil
case "BGT", "BGTU", "BLE", "BLEU":
// The reversed conditional branches: BGT a, b, label is BLT b, a.
if len(ops) != 3 {
return nil, true, fmt.Errorf("%s expects 3 operands, got %d", mnem, len(ops))
}
a := regFromOperand(ops[0])
b := regFromOperand(ops[1])
if a < 0 || b < 0 {
return nil, true, fmt.Errorf("%s: invalid register", mnem)
}
target := labelFromOperand(ops[2])
targetOff, ok := offsets[target]
if !ok {
return nil, true, fmt.Errorf("undefined label %q%s", target, suggestLabel(target, offsets))
}
offset := int32(targetOff - pc)
if err := riscvCheckBranchOffset(target, offset); err != nil {
return nil, true, err
}
var enc riscvEnc
switch mnem {
case "BGT":
enc = riscvEnc{0x63, 0x4, 0x00} // blt b, a
case "BGTU":
enc = riscvEnc{0x63, 0x6, 0x00} // bltu b, a
case "BLE":
enc = riscvEnc{0x63, 0x5, 0x00} // bge b, a
case "BLEU":
enc = riscvEnc{0x63, 0x7, 0x00} // bgeu b, a
}
return wordLE(riscvBType(enc, b, a, offset)), true, nil
// BGT/BGTU/BLE/BLEU have no extended handler: the main table's
// branch path owns them, including the N(PC) forms.
case "FABSD":
// FABSD rs, rd is FSGNJX.D (sign XOR, funct3 2) with the source in
case "FABSS", "FABSD", "FNEGS", "FNEGD":
// INSTR fs, fd: the sign-injection pseudos, the source in both the
// rs1 and rs2 fields (AFABSS: FSGNJXS rs, rs, rd; AFNEGS: FSGNJNS).
// The toolchain pins the bytes: FABSS F0, F1 is 0x200020d3.
if len(ops) != 2 {
return nil, true, fmt.Errorf("FABSD expects 2 operands, got %d", len(ops))
return nil, true, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops))
}
rs := regFromOperand(ops[0])
rd := regFromOperand(ops[1])
if rs < 0 || rd < 0 {
return nil, true, fmt.Errorf("FABSD: invalid register")
return nil, true, fmt.Errorf("%s: invalid register", mnem)
}
return wordLE(riscvRType(riscvEnc{0x53, 0x2, 0x11}, rd, rs, rs)), true, nil
enc := map[string]riscvEnc{
"FABSS": {0x53, 0x2, 0x10}, // fsgnjx.s
"FABSD": {0x53, 0x2, 0x11}, // fsgnjx.d
"FNEGS": {0x53, 0x1, 0x10}, // fsgnjn.s
"FNEGD": {0x53, 0x1, 0x11}, // fsgnjn.d
}[mnem]
return wordLE(riscvRType(enc, rd, rs, rs)), true, nil
case "FNES", "FNED":
// INSTR fs1, fs2, xrd: the not-equal pseudos read as FEQ.S/FEQ.D
// followed by XORI $1 on the result register (AFNES), two words.
if len(ops) != 3 {
return nil, true, fmt.Errorf("%s expects 3 operands, got %d", mnem, len(ops))
}
rs2 := regFromOperand(ops[0])
rs1 := regFromOperand(ops[1])
rd := regFromOperand(ops[2])
if rs1 < 0 || rs2 < 0 || rd < 0 {
return nil, true, fmt.Errorf("%s: invalid register", mnem)
}
feq := riscvEnc{0x53, 0x2, 0x50} // feq.s
if mnem == "FNED" {
feq = riscvEnc{0x53, 0x2, 0x51} // feq.d
}
eq := riscvRType(feq, rd, rs1, rs2)
not := riscvIType(riscvEnc{0x13, 0x4, 0x00}, rd, rd, 1) // xori $1
return []byte{byte(eq), byte(eq >> 8), byte(eq >> 16), byte(eq >> 24),
byte(not), byte(not >> 8), byte(not >> 16), byte(not >> 24)}, true, nil
default:
return encodeRISCVVector(mnem, ops)