rv64: Canonicalize subword divisors
30590736d2a84316b65eb8ec25b11e17da99dfe9e95f03a7e48a49cd5dabed28
Division and remainder tested raw immediates for zero and passed them directly to RV64 instructions. Literals that wrapped at the operation width could therefore avoid the zero trap or use the wrong signed value. Canonicalize divisors to the declared width before both the zero check and the arithmetic instruction. Assisted-by: Codex:gpt-5.6-sol
1 parent
261f5d9f
lib/std/arch/rv64/isel.rad
+21 -8
| 231 | 231 | }, |
|
| 232 | 232 | case il::Type::W64 => {} |
|
| 233 | 233 | } |
|
| 234 | 234 | } |
|
| 235 | 235 | ||
| 236 | - | /// Resolve a value, trap if zero (unless known non-zero), and return the register. |
|
| 237 | - | fn resolveAndTrapIfZero(s: *mut Selector, b: il::Val) -> gen::Reg { |
|
| 238 | - | let rs2 = resolveVal(s, super::SCRATCH2, b); |
|
| 239 | - | let mut knownNonZero = false; |
|
| 236 | + | /// Resolve a divisor in its declared width, trap if it becomes zero, and |
|
| 237 | + | /// return the canonicalized register. |
|
| 238 | + | fn resolveAndTrapIfZero( |
|
| 239 | + | s: *mut Selector, |
|
| 240 | + | b: il::Val, |
|
| 241 | + | typ: il::Type, |
|
| 242 | + | signed: bool |
|
| 243 | + | ) -> gen::Reg { |
|
| 244 | + | let mut divisor = b; |
|
| 240 | 245 | if let case il::Val::Imm(imm) = b { |
|
| 246 | + | set divisor = il::Val::Imm(canonicalCmpImm(imm, typ, signed)); |
|
| 247 | + | } |
|
| 248 | + | let rs2 = resolveVal(s, super::SCRATCH2, divisor); |
|
| 249 | + | if not isExtendedImm(divisor, typ, signed) { |
|
| 250 | + | emitCmpExt(s.e, rs2, rs2, typ, signed); |
|
| 251 | + | } |
|
| 252 | + | let mut knownNonZero = false; |
|
| 253 | + | if let case il::Val::Imm(imm) = divisor { |
|
| 241 | 254 | set knownNonZero = imm <> 0; |
|
| 242 | 255 | } |
|
| 243 | 256 | if not knownNonZero { |
|
| 244 | 257 | emit::emit(s.e, encode::bne(rs2, super::ZERO, super::INSTR_SIZE * 2)); |
|
| 245 | 258 | emit::emit(s.e, encode::ebreak()); |
| 877 | 890 | encode::mulw(rd, rs1, rs2) |
|
| 878 | 891 | if typ == il::Type::W32 else |
|
| 879 | 892 | encode::mul(rd, rs1, rs2)); |
|
| 880 | 893 | } |
|
| 881 | 894 | case il::BinOp::Sdiv => { |
|
| 882 | - | let rs2 = resolveAndTrapIfZero(s, b); |
|
| 895 | + | let rs2 = resolveAndTrapIfZero(s, b, typ, true); |
|
| 883 | 896 | emit::emit(s.e, |
|
| 884 | 897 | encode::divw(rd, rs1, rs2) |
|
| 885 | 898 | if typ == il::Type::W32 else |
|
| 886 | 899 | encode::div(rd, rs1, rs2)); |
|
| 887 | 900 | } |
|
| 888 | 901 | case il::BinOp::Udiv => { |
|
| 889 | - | let rs2 = resolveAndTrapIfZero(s, b); |
|
| 902 | + | let rs2 = resolveAndTrapIfZero(s, b, typ, false); |
|
| 890 | 903 | emit::emit(s.e, |
|
| 891 | 904 | encode::divuw(rd, rs1, rs2) |
|
| 892 | 905 | if typ == il::Type::W32 else |
|
| 893 | 906 | encode::divu(rd, rs1, rs2)); |
|
| 894 | 907 | } |
|
| 895 | 908 | case il::BinOp::Srem => { |
|
| 896 | - | let rs2 = resolveAndTrapIfZero(s, b); |
|
| 909 | + | let rs2 = resolveAndTrapIfZero(s, b, typ, true); |
|
| 897 | 910 | emit::emit(s.e, |
|
| 898 | 911 | encode::remw(rd, rs1, rs2) |
|
| 899 | 912 | if typ == il::Type::W32 else |
|
| 900 | 913 | encode::rem(rd, rs1, rs2)); |
|
| 901 | 914 | } |
|
| 902 | 915 | case il::BinOp::Urem => { |
|
| 903 | - | let rs2 = resolveAndTrapIfZero(s, b); |
|
| 916 | + | let rs2 = resolveAndTrapIfZero(s, b, typ, false); |
|
| 904 | 917 | emit::emit(s.e, |
|
| 905 | 918 | encode::remuw(rd, rs1, rs2) |
|
| 906 | 919 | if typ == il::Type::W32 else |
|
| 907 | 920 | encode::remu(rd, rs1, rs2)); |
|
| 908 | 921 | } |
test/tests/div.u8.immediate.wrap.rad
added
+10 -0
| 1 | + | //! returns: 133 |
|
| 2 | + | //! A u8 divisor literal of 256 wraps to zero and must trap. |
|
| 3 | + | ||
| 4 | + | fn divideByWrappedZero(value: u8) -> u8 { |
|
| 5 | + | return value / 256; |
|
| 6 | + | } |
|
| 7 | + | ||
| 8 | + | @default fn main() -> u8 { |
|
| 9 | + | return divideByWrappedZero(10); |
|
| 10 | + | } |