From b9dfb48d79b91044fa0e707410fa0b1018550a14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Balv=C3=ADn?= Date: Wed, 7 Oct 2026 13:03:20 +0200 Subject: [PATCH] fix(asm): encode the loong64 64-bit-span 2RI14 offsets like the toolchain Assisted-by: GLM 5.3 --- asm/loong64_assemble.go | 32 ++++++++++++++++++++++++++++---- asm/loong64_encode.go | 5 ++--- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/asm/loong64_assemble.go b/asm/loong64_assemble.go index d30ffd2..0fbc842 100644 --- a/asm/loong64_assemble.go +++ b/asm/loong64_assemble.go @@ -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") } diff --git a/asm/loong64_encode.go b/asm/loong64_encode.go index 73cd035..c5d4f82 100644 --- a/asm/loong64_encode.go +++ b/asm/loong64_encode.go @@ -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: