fix(asm): encode the loong64 64-bit-span 2RI14 offsets like the toolchain

Assisted-by: GLM 5.3
This commit is contained in:
petrbalvin committed 2026-10-07 13:51:02 +02:00
1 parent 41aa8edfd2
commit b9dfb48d79
2 files changed
+30 -7

No files matched your search

+28 -4
View File
@@ -825,6 +825,22 @@ func encodeLOONG64Instr(instr *ast.Instr, pc int, offsets map[string]int, fi loo
if off&3 != 0 {
return nil, fmt.Errorf("%s: offset must be a multiple of 4", mnem)
}
// The 64-bit span reproduces the toolchain's case 73/74 size-24
// expansion word for word, its missing load negation included: a
// MOVWP/MOVVP load emits the store opcode, because case 74 spells
// opirr(p.As) where the narrower spans spell opirr(-p.As), and the
// LL family has no unnegated opirr entry at all, so the toolchain
// refuses those loads with "bad irr opcode".
if off < -2147483650 || off >= 2147483646 {
if load {
switch mnem {
case "MOVWP", "MOVVP":
op += 1 << 24
default:
return nil, fmt.Errorf("bad irr opcode %s", mnem)
}
}
}
return l64firr14Words(op, int(off), rj, rd), nil
case l64Fir20:
@@ -1292,21 +1308,29 @@ func l64FmaOperands(ops []*ast.Operand) (fa, fk, fj, fd int, err error) {
// l64MemOperands extracts (rd, rj, off, load) from a load/store instruction:
// INSTR mem, rd is a load, INSTR rd, mem a store.
// l64MemOperands splits the two-operand load/store forms: the register and
// the memory side, the memory's base and byte offset, and the direction.
func l64MemOperands(ops []*ast.Operand, fi loong64FrameInfo) (rd, rj int, off int32, load bool, err error) {
// the memory side, the memory's base and full-width byte offset (64-bit
// offsets reach the 2RI14 families' six-word span), and the direction.
func l64MemOperands(ops []*ast.Operand, fi loong64FrameInfo) (rd, rj int, off int64, load bool, err error) {
if len(ops) != 2 {
return 0, 0, 0, false, fmt.Errorf("expected 2 operands, got %d", len(ops))
}
var mem *ast.Operand
if isMemOperand(ops[0]) {
rd = l64Reg(ops[1])
rj, off = l64MemWithFrame(ops[0], fi)
mem = ops[0]
load = true
} else if isMemOperand(ops[1]) {
rd = l64Reg(ops[0])
rj, off = l64MemWithFrame(ops[1], fi)
mem = ops[1]
} else {
return 0, 0, 0, false, fmt.Errorf("expected a memory operand")
}
if mem.Addr.Sym != nil && mem.Addr.Sym.Pseudo != "" {
pj, poff := loong64ResolvePseudo(mem.Addr.Sym, fi)
rj, off = pj, int64(poff)
} else {
rj, off = loong64RegNum(mem.Addr.Base), mem.Addr.Offset
}
if rd < 0 || rj < 0 {
return 0, 0, 0, false, fmt.Errorf("invalid operand")
}
+2 -3
View File
@@ -264,9 +264,8 @@ func l64WordsLE(ws ...uint32) []byte {
// a 32-bit one (C_LOREG_32) splits between addu16i.d (bits 31:16, materialised
// in R30, the assembler temp) and si14 (bits 15:2); anything wider
// (C_LOREG_64) builds the whole constant in R30 through lu12i.w + ori +
// lu32i.d + lu52i.d and folds the base into it. The loong64 backend resolves
// memory offsets as int32, so the third span answers nothing the encoder can
// ask today: it stands so the mapping stays complete if that domain widens.
// lu32i.d + lu52i.d and folds the base into it. The caller adjusts the
// opcode for the 64-bit span's load-direction quirks before arriving here.
func l64firr14Words(op uint32, off, rj, rd int) []byte {
switch {
case off >= -32766 && off < 32766: