fix(asm): carry the riscv64 U-type immediate raw
The toolchain writes the source immediate straight into imm[31:12] (riscv64.s: AUIPC 24287, X10 encodes 7ffff517), and rejects values beyond the signed 20-bit span; the encoder divided by 4096 instead and truncated silently, so the high bits of every large AUIPC and LUI were lost. Assisted-by: GLM 5.3 Flash
This commit is contained in:
1 parent
a829b8f321
commit
0cfed5516c
2 files changed
+36
-2
No files matched your search
@@ -1751,7 +1751,10 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
|
|||||||
// The Go assembler never compresses branches to C.BEQZ/C.BNEZ.
|
// The Go assembler never compresses branches to C.BEQZ/C.BNEZ.
|
||||||
word = riscvBType(enc, rs1, rs2, offset)
|
word = riscvBType(enc, rs1, rs2, offset)
|
||||||
|
|
||||||
// U-type: rd, imm (or the toolchain testdata's INSTR $imm, rd).
|
// U-type: rd, imm (or the toolchain testdata's INSTR $imm, rd). The
|
||||||
|
// immediate rides the field raw (riscv64.s: AUIPC $524287, X10 encodes
|
||||||
|
// 7ffff517), so it shifts into imm[31:12] here, and the span is the
|
||||||
|
// signed 20-bit range the toolchain checks.
|
||||||
case len(ops) == 2 && isUTypeInstr(mnem):
|
case len(ops) == 2 && isUTypeInstr(mnem):
|
||||||
var rd int
|
var rd int
|
||||||
var imm int32
|
var imm int32
|
||||||
@@ -1763,7 +1766,10 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
|
|||||||
if rd < 0 {
|
if rd < 0 {
|
||||||
return nil, fmt.Errorf("invalid register in %s", mnem)
|
return nil, fmt.Errorf("invalid register in %s", mnem)
|
||||||
}
|
}
|
||||||
word = riscvUType(enc, rd, imm)
|
if imm < -(1<<19) || imm > (1<<19)-1 {
|
||||||
|
return nil, fmt.Errorf("%s: signed immediate 0x%x must be in range [-0x80000, 0x7ffff] (20 bits)", mnem, imm)
|
||||||
|
}
|
||||||
|
word = riscvUType(enc, rd, imm<<12)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("cannot encode %s with %d operands", mnem, len(ops))
|
return nil, fmt.Errorf("cannot encode %s with %d operands", mnem, len(ops))
|
||||||
|
|||||||
@@ -109,6 +109,34 @@ TEXT ·imm(SB), NOSPLIT, $0
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestRISCV_utypeImmediate pins the U-type immediate semantics byte for byte
|
||||||
|
// against go tool asm (riscv64.s lines 76 to 85): the source immediate is the
|
||||||
|
// raw 20-bit field, not a byte address to divide, and both sign extremes
|
||||||
|
// encode. The range beyond the signed 20 bits is the toolchain's own error.
|
||||||
|
func TestRISCV_utypeImmediate(t *testing.T) {
|
||||||
|
fn := firstTextRISCV(t, `#include "textflag.h"
|
||||||
|
TEXT ·utype(SB), NOSPLIT, $0
|
||||||
|
AUIPC $524287, X10
|
||||||
|
LUI $524287, X15
|
||||||
|
LUI $167, X15
|
||||||
|
AUIPC $-524288, X15
|
||||||
|
RET
|
||||||
|
`)
|
||||||
|
code := assembleRISCVHelper(t, fn)
|
||||||
|
want := "17f5ff7f" + "b7f7ff7f" + "b7770a00" + "97070080"
|
||||||
|
if got := hex.EncodeToString(code[:16]); got != want {
|
||||||
|
t.Errorf("U-type immediates: %s, want %s", got, want)
|
||||||
|
}
|
||||||
|
fn = firstTextRISCV(t, `#include "textflag.h"
|
||||||
|
TEXT ·wide(SB), NOSPLIT, $0
|
||||||
|
AUIPC $524288, X10
|
||||||
|
RET
|
||||||
|
`)
|
||||||
|
if _, _, _, _, _, _, err := assembleRISCV(fn); err == nil {
|
||||||
|
t.Error("AUIPC $524288: expected the 20-bit range error, got none")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRISCV_branches(t *testing.T) {
|
func TestRISCV_branches(t *testing.T) {
|
||||||
fn := firstTextRISCV(t, `#include "textflag.h"
|
fn := firstTextRISCV(t, `#include "textflag.h"
|
||||||
TEXT ·br(SB), NOSPLIT, $0
|
TEXT ·br(SB), NOSPLIT, $0
|
||||||
|
|||||||
Reference in new issue
Block a user