Fix a small instruction selection bug
f1bc2ebd6a8a7a165610be02c4c4e5a97e33d699f67f8e28188bcbd041045f34
1 parent
7bfd64dd
lib/std/arch/rv64/emit.rad
+1 -1
| 490 | 490 | pub fn emitLoad(e: *mut Emitter, rd: super::Reg, base: super::Reg, offset: i32, typ: il::Type) { |
|
| 491 | 491 | let adj = adjustOffset(e, base, offset); |
|
| 492 | 492 | match typ { |
|
| 493 | 493 | case il::Type::W8 => emit(e, encode::lbu(rd, adj.base, adj.offset)), |
|
| 494 | 494 | case il::Type::W16 => emit(e, encode::lhu(rd, adj.base, adj.offset)), |
|
| 495 | - | case il::Type::W32 => emit(e, encode::lw(rd, adj.base, adj.offset)), |
|
| 495 | + | case il::Type::W32 => emit(e, encode::lwu(rd, adj.base, adj.offset)), |
|
| 496 | 496 | case il::Type::W64 => emit(e, encode::ld(rd, adj.base, adj.offset)), |
|
| 497 | 497 | } |
|
| 498 | 498 | } |
|
| 499 | 499 | ||
| 500 | 500 | /// Emit signed load with automatic offset adjustment. |
test/tests/load.u32.high.rad
added
+60 -0
| 1 | + | //! returns: 1 |
|
| 2 | + | //! Test that u32 values with bit 31 set are correctly zero-extended |
|
| 3 | + | //! when loaded from memory. |
|
| 4 | + | //! |
|
| 5 | + | //! Regression test: `lw` sign-extends on RV64, producing a negative |
|
| 6 | + | //! 64-bit value for u32 values >= 0x80000000. The correct instruction |
|
| 7 | + | //! is `lwu` which zero-extends. |
|
| 8 | + | ||
| 9 | + | /// A u32 value with bit 31 set, stored in a struct to force a memory load. |
|
| 10 | + | record Pair { |
|
| 11 | + | lo: u32, |
|
| 12 | + | hi: u32, |
|
| 13 | + | } |
|
| 14 | + | ||
| 15 | + | /// Test that a u32 field with bit 31 set compares correctly. |
|
| 16 | + | fn testFieldCompare() -> bool { |
|
| 17 | + | let p = Pair { lo: 1, hi: 0x80000000 }; |
|
| 18 | + | ||
| 19 | + | // With lw (sign-extend), p.hi is loaded as 0xFFFFFFFF80000000 |
|
| 20 | + | // in the register. A subsequent u32 comparison should still work |
|
| 21 | + | // because the comparison normalizes, but let's test the value |
|
| 22 | + | // through arithmetic that would expose the sign-extension. |
|
| 23 | + | let val = p.hi; |
|
| 24 | + | ||
| 25 | + | // Shift right by 1: if correctly zero-extended (0x80000000), |
|
| 26 | + | // u32 shift gives 0x40000000. |
|
| 27 | + | // If sign-extended to 64-bit and then shifted as u32 via `srliw`, |
|
| 28 | + | // the *w variant only looks at low 32 bits, so this still works. |
|
| 29 | + | let shifted = val >> 1; |
|
| 30 | + | if shifted != 0x40000000 { |
|
| 31 | + | return false; |
|
| 32 | + | } |
|
| 33 | + | return true; |
|
| 34 | + | } |
|
| 35 | + | ||
| 36 | + | /// Test u32 max value round-trips through memory correctly. |
|
| 37 | + | fn testMaxU32() -> bool { |
|
| 38 | + | let arr: [u32; 1] = [0xFFFFFFFF]; |
|
| 39 | + | let val = arr[0]; |
|
| 40 | + | ||
| 41 | + | // If lw sign-extends, val in register is 0xFFFFFFFFFFFFFFFF. |
|
| 42 | + | // Adding 1 as u32 (via addw): 0xFFFFFFFF + 1 = 0 (wraps). OK either way. |
|
| 43 | + | // But comparing: val should equal 0xFFFFFFFF as u32. |
|
| 44 | + | if val != 0xFFFFFFFF { |
|
| 45 | + | return false; |
|
| 46 | + | } |
|
| 47 | + | ||
| 48 | + | // The key test: widening to u64 should give 0x00000000FFFFFFFF, |
|
| 49 | + | // not 0xFFFFFFFFFFFFFFFF. |
|
| 50 | + | let wide: u64 = val as u64; |
|
| 51 | + | if wide != 0xFFFFFFFF { |
|
| 52 | + | return false; |
|
| 53 | + | } |
|
| 54 | + | ||
| 55 | + | return true; |
|
| 56 | + | } |
|
| 57 | + | ||
| 58 | + | @default fn main() -> bool { |
|
| 59 | + | return testFieldCompare() and testMaxU32(); |
|
| 60 | + | } |