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:
1 parent
c3540f0549
commit
d82ef33fa9
2 files changed
+59
-5
No files matched your search
+30
-5
@@ -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":
|
||||
|
||||
@@ -1389,3 +1389,32 @@ func TestRISCV_rawDataRange(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestRISCV_shiftImmediateRange pins the shift immediate at the toolchain's
|
||||
// validation boundary: 0-63 on the doubleword forms, 0-31 on the word forms,
|
||||
// values beyond rejected on sight rather than masked into the field.
|
||||
func TestRISCV_shiftImmediateRange(t *testing.T) {
|
||||
asmOne := func(t *testing.T, stmt string) error {
|
||||
t.Helper()
|
||||
fn := firstTextRISCV(t, "#include \"textflag.h\"\nTEXT ·s(SB), NOSPLIT, $0\n\t"+stmt+"\n\tRET\n")
|
||||
_, _, _, _, _, _, err := assembleRISCV(fn)
|
||||
return err
|
||||
}
|
||||
for _, s := range []string{
|
||||
"SLLI $63, X5, X6", "SLLI $0, X5, X6", "SRLI $63, X5", "SRAI $1, X5, X6",
|
||||
"SLLIW $31, X5, X6", "SRLIW $31, X5", "SRAIW $1, X5, X6",
|
||||
} {
|
||||
if err := asmOne(t, s); err != nil {
|
||||
t.Errorf("%s must assemble: %v", s, err)
|
||||
}
|
||||
}
|
||||
for _, s := range []string{
|
||||
"SLLI $64, X5, X6", "SLLI $-1, X5", "SLLI $0x100000000, X5, X6",
|
||||
"SRLI $64, X5", "SRAI $-1, X5, X6",
|
||||
"SLLIW $32, X5, X6", "SRLIW $-1, X5", "SRAIW $32, X5, X6",
|
||||
} {
|
||||
if err := asmOne(t, s); err == nil {
|
||||
t.Errorf("%s must be rejected, as go tool asm rejects it", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user