fix(asm): bound the riscv64 shift immediate at the instruction width

SLLI $64 assembled with the amount silently masked into the six-bit
field where the toolchain rejects it, and the word forms took 0-63 where
they take 0-31.  Both families now validate against their own width and
the check reads the immediate at full width, so a value the source
spelled beyond int32 cannot wrap into the range; the boundary is pinned
in a test.

Assisted-by: GLM 5.3 Flash
This commit is contained in:
petrbalvin committed 2026-10-07 00:47:27 +02:00
1 parent c3540f0549
commit d82ef33fa9
2 files changed
+59 -5

No files matched your search

+30 -5
View File
@@ -1459,23 +1459,37 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
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.
// form INSTR $shamt, rd uses rd as the source. The shift amount is
// bounded at the instruction width, as the toolchain validates it: 0-63
// for the doubleword forms, 0-31 for the word forms.
case len(ops) == 3 && isShiftImmInstr(mnem):
shamt := int(immFromOperand(ops[0]))
shamt, ok := riscvRawImm(ops[0])
if !ok {
return nil, fmt.Errorf("%s expects an immediate shift amount", mnem)
}
if hi := riscvShiftMax(mnem); shamt < 0 || shamt > hi {
return nil, fmt.Errorf("%s: immediate %d out of range 0 to %d", mnem, shamt, hi)
}
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)
word = riscvRType(enc, rd, rs1, int(shamt))
case len(ops) == 2 && isShiftImmInstr(mnem):
shamt := int(immFromOperand(ops[0]))
shamt, ok := riscvRawImm(ops[0])
if !ok {
return nil, fmt.Errorf("%s expects an immediate shift amount", mnem)
}
if hi := riscvShiftMax(mnem); shamt < 0 || shamt > hi {
return nil, fmt.Errorf("%s: immediate %d out of range 0 to %d", mnem, shamt, hi)
}
rd := regFromOperand(ops[1])
if rd < 0 {
return nil, fmt.Errorf("invalid register in %s", mnem)
}
word = riscvRType(enc, rd, rd, shamt)
word = riscvRType(enc, rd, rd, int(shamt))
// AMO atomics: Plan 9 order is INSTR src, (addr), dst.
case len(ops) == 3 && isAMOInstr(mnem):
@@ -3150,6 +3164,17 @@ func isShiftImmInstr(m string) bool {
return false
}
// riscvShiftMax bounds a shift immediate at the instruction's width: the
// doubleword forms shift 0-63, the word forms 0-31, the toolchain's own
// validation boundary.
func riscvShiftMax(m string) int64 {
switch m {
case "SLLIW", "SRLIW", "SRAIW":
return 31
}
return 63
}
func isITypeInstr(m string) bool {
switch m {
case "ADDI", "ADDIW", "SLTI", "SLTIU", "XORI", "ORI", "ANDI", "JALR":