compiler/
lib/
examples/
std/
arch/
rv64/
asm/
image/
tests.rad
11.6 KiB
shared/
asm.rad
24.8 KiB
atomicTests.rad
5.0 KiB
atomics.rad
3.6 KiB
bounds.rad
21.0 KiB
decode.rad
15.2 KiB
emit.rad
33.2 KiB
encode.rad
22.0 KiB
image.rad
4.5 KiB
isel.rad
52.1 KiB
printer.rad
13.9 KiB
shared.rad
16.8 KiB
tests.rad
17.2 KiB
rv64.rad
17.5 KiB
char/
collections/
lang/
sys/
arch.rad
68 B
char.rad
855 B
collections.rad
39 B
fmt.rad
8.3 KiB
intrinsics.rad
467 B
io.rad
1.7 KiB
lang.rad
276 B
mem.rad
2.3 KiB
sys.rad
179 B
testing.rad
2.4 KiB
tests.rad
15.7 KiB
vec.rad
3.2 KiB
std.rad
281 B
scripts/
seed/
sublime/
test/
vim/
.gitignore
336 B
.gitsigners
112 B
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
5.4 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
662 B
lib/std/arch/rv64/image/tests.rad
raw
| 1 | //! Native image placement, relocation, and wire-header checks. |
| 2 | |
| 3 | use std::testing; |
| 4 | use std::lang::il; |
| 5 | use std::lang::alloc; |
| 6 | use std::lang::gen::data; |
| 7 | use std::lang::gen::labels; |
| 8 | use std::collections::dict; |
| 9 | use std::arch::rv64; |
| 10 | use std::arch::rv64::image; |
| 11 | use std::arch::rv64::emit; |
| 12 | use std::arch::rv64::encode; |
| 13 | |
| 14 | /// Arena for one emitter and its bounded relocation tables. |
| 15 | static MEMORY: [u8; 16777216] = [0; 16777216]; |
| 16 | /// Data symbol lookup workspace. |
| 17 | unsafe static ENTRIES: [dict::Entry; data::DATA_SYM_TABLE_SIZE] = undefined; |
| 18 | |
| 19 | /// Scalar data emission preserves widths, signs, and little-endian order. |
| 20 | @test fn safeDataEmission() throws (testing::TestError) { |
| 21 | let symbols = data::DataSymMap { dict: dict::init(&mut []), syms: &[] }; |
| 22 | let functions = labels::init(&mut [], &mut []); |
| 23 | let item = il::Data { |
| 24 | name: "scalar", size: 19, alignment: 1, readOnly: true, isZeroInit: false, |
| 25 | values: &[ |
| 26 | il::DataValue { item: il::DataItem::Val { typ: il::Type::W8, val: -1 }, count: 2 }, |
| 27 | il::DataValue { item: il::DataItem::Val { typ: il::Type::W16, val: 0x1234 }, count: 1 }, |
| 28 | il::DataValue { item: il::DataItem::Val { typ: il::Type::W32, val: 0x12345678 }, count: 1 }, |
| 29 | il::DataValue { item: il::DataItem::Val { typ: il::Type::W64, val: -2 }, count: 1 }, |
| 30 | il::DataValue { item: il::DataItem::Str("ok"), count: 1 }, |
| 31 | il::DataValue { item: il::DataItem::Undef, count: 1 }, |
| 32 | ], |
| 33 | }; |
| 34 | let mut buffer: [u8; 21] = [42; 21]; |
| 35 | let size = try data::emitSection(&[item], &symbols, &functions, 0, &mut buffer[1..20], true) catch { |
| 36 | throw testing::TestError::Failed; |
| 37 | }; |
| 38 | try testing::expect(size == 19); |
| 39 | try testing::expectBytesEq(&buffer[..], &[ |
| 40 | 42, 255, 255, 52, 18, 120, 86, 52, 18, |
| 41 | 254, 255, 255, 255, 255, 255, 255, 255, 111, 107, 0, 42, |
| 42 | ]); |
| 43 | let mut failed = false; |
| 44 | try data::emitSection(&[item], &symbols, &functions, 0, &mut buffer[..18], true) catch err { |
| 45 | try testing::expect(err == data::Error::Capacity); |
| 46 | set failed = true; |
| 47 | }; |
| 48 | try testing::expect(failed); |
| 49 | } |
| 50 | |
| 51 | /// Finalization preserves hosted placement and propagates bounded failures. |
| 52 | @test unsafe fn programFinalization() throws (testing::TestError) { |
| 53 | for scenario in 0..6 { |
| 54 | let mut arena = alloc::new(&mut MEMORY[..]); |
| 55 | let placement = image::Placement::Physical { |
| 56 | code: 0x80000000, roData: 0x80000000, rwData: 0x80002000, entry: 0x80000000, |
| 57 | } if scenario == 5 else image::Placement::Hosted; |
| 58 | let entryPatch = rv64::EntryPatch::Reserved(nil) if scenario == 2 else rv64::EntryPatch::None; |
| 59 | let mut generator = try! rv64::beginProgram(rv64::ProgramOptions { |
| 60 | entryPatch, debug: false, placement, |
| 61 | }, &mut arena); |
| 62 | emit::emit(&mut generator.e, encode::nop()); |
| 63 | if scenario == 3 { set generator.e.error = rv64::Error::Capacity; } |
| 64 | if scenario == 4 { emit::recordCall(&mut generator.e, "missing"); } |
| 65 | let mut ro: [u8; 5] = [42; 5]; |
| 66 | let mut rw: [u8; 0] = []; |
| 67 | let capacity: u32 = 2 if scenario == 1 else 3; |
| 68 | let result = try rv64::finishProgram(generator, &[], |
| 69 | rv64::Storage { dataSyms: &mut [], dataSymEntries: &mut ENTRIES[..] }, |
| 70 | &[7, 8, 9], &mut ro[1..capacity + 1], &mut rw[..] |
| 71 | ) catch err { |
| 72 | // Record the failure before the next independent generator is built. |
| 73 | let expected = rv64::Error::Image(image::Error::Overlap) if scenario == 5 |
| 74 | else rv64::Error::Symbol if scenario == 2 or scenario == 4 |
| 75 | else rv64::Error::Capacity; |
| 76 | assert err == expected; |
| 77 | assert scenario <> 0; |
| 78 | assert ro[0] == 42 and ro[capacity + 1] == 42; |
| 79 | continue; |
| 80 | }; |
| 81 | assert scenario == 0; |
| 82 | assert result.layout.code.address == rv64::RO_DATA_BASE as u64 + 8; |
| 83 | assert result.code.len == 1 and result.code[0] == encode::nop(); |
| 84 | assert result.roDataSize == 3 and result.rwDataSize == 0; |
| 85 | assert ro[0] == 42 and ro[4] == 42; |
| 86 | assert ro[1] == 7 and ro[2] == 8 and ro[3] == 9; |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | /// Construct a small image with three disjoint high-address segments. |
| 91 | fn layout() -> image::Layout { |
| 92 | return image::Layout { |
| 93 | entry: 0x80000004, |
| 94 | code: image::Segment { address: 0x80000000, initialized: 8, memory: 8 }, |
| 95 | roData: image::Segment { address: 0x80001000, initialized: 3, memory: 16 }, |
| 96 | rwData: image::Segment { address: 0x80002000, initialized: 4, memory: 4096 }, |
| 97 | }; |
| 98 | } |
| 99 | |
| 100 | /// Check the exact 64-byte little-endian header. |
| 101 | @test fn header() throws (testing::TestError) { |
| 102 | let bytes = try image::header(layout()) catch { |
| 103 | throw testing::TestError::Failed; |
| 104 | }; |
| 105 | try testing::expectBytesEq(&bytes[..], &[ |
| 106 | 82, 65, 68, 48, 2, 0, 0, 0, 4, 0, 0, 128, 0, 0, 0, 0, |
| 107 | 0, 0, 0, 128, 0, 0, 0, 0, 8, 0, 0, 0, 8, 0, 0, 0, |
| 108 | 0, 16, 0, 128, 0, 0, 0, 0, 3, 0, 0, 0, 16, 0, 0, 0, |
| 109 | 0, 32, 0, 128, 0, 0, 0, 0, 4, 0, 0, 0, 0, 16, 0, 0, |
| 110 | ]); |
| 111 | } |
| 112 | |
| 113 | /// Check a rejected image layout. |
| 114 | fn invalid(item: image::Layout, expected: image::Error) throws (testing::TestError) { |
| 115 | let mut failed = false; |
| 116 | try image::validate(item) catch err { |
| 117 | try testing::expect(err == expected); |
| 118 | set failed = true; |
| 119 | }; |
| 120 | try testing::expect(failed); |
| 121 | } |
| 122 | |
| 123 | /// Check overlap, alignment, size, entry, and address overflow failures. |
| 124 | @test fn validation() throws (testing::TestError) { |
| 125 | let mut item = layout(); |
| 126 | set item.rwData.address = item.roData.address + 8; |
| 127 | try invalid(item, image::Error::Overlap); |
| 128 | set item = layout(); |
| 129 | set item.roData.address = item.code.address; |
| 130 | try invalid(item, image::Error::Overlap); |
| 131 | set item = layout(); |
| 132 | set item.rwData.address += 1; |
| 133 | try invalid(item, image::Error::Alignment); |
| 134 | set item = layout(); |
| 135 | set item.entry += 1; |
| 136 | try invalid(item, image::Error::Alignment); |
| 137 | set item = layout(); |
| 138 | set item.entry = item.code.address + item.code.initialized as u64; |
| 139 | try invalid(item, image::Error::Entry); |
| 140 | set item = layout(); |
| 141 | set item.roData.initialized = 17; |
| 142 | try invalid(item, image::Error::Size); |
| 143 | set item = layout(); |
| 144 | set item.rwData.address = 0xfffffffffffffff8; |
| 145 | try invalid(item, image::Error::Overflow); |
| 146 | set item = layout(); |
| 147 | set item.roData.address = item.code.address + 8; |
| 148 | try image::validate(item) catch { |
| 149 | throw testing::TestError::Failed; |
| 150 | }; |
| 151 | } |
| 152 | |
| 153 | /// Check AUIPC/ADDI limits without overflowing unsigned address arithmetic. |
| 154 | @test fn displacements() throws (testing::TestError) { |
| 155 | try testing::expect(image::displacement(0x180000000, 0x180002000) == 8192); |
| 156 | try testing::expect(image::displacement(0x180002000, 0x180000000) == -8192); |
| 157 | try testing::expect(image::displacement(0, 0x7ffff7ff) == 0x7ffff7ff); |
| 158 | try testing::expect(image::displacement(0, 0x7ffff800) == nil); |
| 159 | try testing::expect(image::displacement(0x80000000, 0) == -2147483648); |
| 160 | try testing::expect(image::displacement(0x80000001, 0) == nil); |
| 161 | try testing::expect(image::displacement(0, 0xffffffffffffffff) == nil); |
| 162 | try testing::expect(image::displacement(0xffffffffffffffff, 0) == nil); |
| 163 | } |
| 164 | |
| 165 | /// Check high physical placement, zero-fill extents, and 64-bit data relocations. |
| 166 | @test unsafe fn nativeProgram() throws (testing::TestError) { |
| 167 | let mut arena = alloc::new(&mut MEMORY[..]); |
| 168 | let mut generator = try! rv64::beginProgram(rv64::ProgramOptions { |
| 169 | entryPatch: rv64::EntryPatch::None, debug: false, |
| 170 | placement: image::Placement::Physical { |
| 171 | code: 0x180000000, roData: 0x180001000, rwData: 0x180002000, entry: 0x180000000, |
| 172 | }, |
| 173 | }, &mut arena); |
| 174 | emit::recordFunc(&mut generator.e, "p::entry"); |
| 175 | emit::recordFuncOffset(&mut generator.e, "p::entry"); |
| 176 | emit::recordDataAddrLoad(&mut generator.e, "p::pointer", rv64::A0); |
| 177 | let globals = &[ |
| 178 | il::Data { |
| 179 | name: "p::pointer", size: 8, alignment: 8, readOnly: false, isZeroInit: false, |
| 180 | values: &[il::DataValue { item: il::DataItem::Fn("p::entry"), count: 1 }], |
| 181 | }, |
| 182 | il::Data { |
| 183 | name: "p::zero", size: 4096, alignment: 8, readOnly: false, isZeroInit: true, values: &[], |
| 184 | }, |
| 185 | ]; |
| 186 | unsafe static syms: [data::DataSym; 2] = undefined; |
| 187 | let mut ro: [u8; 8] = [0; 8]; |
| 188 | let mut rw: [u8; 8] = [0; 8]; |
| 189 | let result = try rv64::finishProgram(generator, globals, |
| 190 | rv64::Storage { dataSyms: &mut syms[..], dataSymEntries: &mut ENTRIES[..] }, |
| 191 | &[], &mut ro[..], &mut rw[..] |
| 192 | ) catch { |
| 193 | throw testing::TestError::Failed; |
| 194 | }; |
| 195 | try testing::expect(result.layout.code.address == 0x180000000); |
| 196 | try testing::expect(result.layout.rwData.initialized == 8); |
| 197 | try testing::expect(result.layout.rwData.memory == 4104); |
| 198 | try testing::expect(syms[1].addr == 0x180002008); |
| 199 | try testing::expectBytesEq(&rw[..], &[0, 0, 0, 128, 1, 0, 0, 0]); |
| 200 | try testing::expect(result.code[0] == encode::auipc(rv64::A0, 2)); |
| 201 | try testing::expect(result.code[1] == encode::addi(rv64::A0, rv64::A0, 0)); |
| 202 | } |
| 203 | |
| 204 | /// Check bounded data-layout failure without wrapped addresses or symbol writes. |
| 205 | @test unsafe fn dataLayout() throws (testing::TestError) { |
| 206 | let item = il::Data { |
| 207 | name: "p::data", size: 16, alignment: 8, readOnly: false, isZeroInit: true, values: &[], |
| 208 | }; |
| 209 | unsafe static syms: [data::DataSym; 1] = undefined; |
| 210 | let mut count: u32 = 0; |
| 211 | let mut failed = false; |
| 212 | try data::layoutSection(&[item], &mut syms[..], &mut count, 0xfffffffffffffff8, false) catch err { |
| 213 | try testing::expect(err == data::Error::Overflow); |
| 214 | set failed = true; |
| 215 | }; |
| 216 | try testing::expect(failed and count == 0); |
| 217 | set failed = false; |
| 218 | try data::layoutSection(&[item], &mut syms[..0], &mut count, 0x80000000, false) catch err { |
| 219 | try testing::expect(err == data::Error::Capacity); |
| 220 | set failed = true; |
| 221 | }; |
| 222 | try testing::expect(failed and count == 0); |
| 223 | set failed = false; |
| 224 | try data::layoutSectionAtOffset(&[item], &mut syms[..], &mut count, 0, 0xfffffff8, false) catch err { |
| 225 | try testing::expect(err == data::Error::Overflow); |
| 226 | set failed = true; |
| 227 | }; |
| 228 | try testing::expect(failed and count == 0); |
| 229 | } |
| 230 | |
| 231 | /// Keep declared symbol extents and alignment gaps in initialized data. |
| 232 | @test unsafe fn initializedExtents() throws (testing::TestError) { |
| 233 | let mut arena = alloc::new(&mut MEMORY[..]); |
| 234 | let mut generator = try! rv64::beginProgram(rv64::ProgramOptions { |
| 235 | entryPatch: rv64::EntryPatch::None, debug: false, |
| 236 | placement: image::Placement::Physical { |
| 237 | code: 0x80000000, roData: 0x80001000, rwData: 0x80002000, entry: 0x80000000, |
| 238 | }, |
| 239 | }, &mut arena); |
| 240 | emit::recordDataAddrLoad(&mut generator.e, "p::second", rv64::A0); |
| 241 | let globals = &[ |
| 242 | il::Data { |
| 243 | name: "p::first", size: 5, alignment: 1, readOnly: false, isZeroInit: false, |
| 244 | values: &[il::DataValue { item: il::DataItem::Str("a"), count: 1 }], |
| 245 | }, |
| 246 | il::Data { |
| 247 | name: "p::second", size: 8, alignment: 8, readOnly: false, isZeroInit: false, |
| 248 | values: &[il::DataValue { item: il::DataItem::Str("b"), count: 1 }], |
| 249 | }, |
| 250 | ]; |
| 251 | unsafe static syms: [data::DataSym; 2] = undefined; |
| 252 | let mut ro: [u8; 0] = []; |
| 253 | let mut rw: [u8; 16] = [255; 16]; |
| 254 | let result = try rv64::finishProgram(generator, globals, |
| 255 | rv64::Storage { dataSyms: &mut syms[..], dataSymEntries: &mut ENTRIES[..] }, |
| 256 | &[], &mut ro[..], &mut rw[..] |
| 257 | ) catch { |
| 258 | throw testing::TestError::Failed; |
| 259 | }; |
| 260 | try testing::expect(result.layout.rwData.initialized == 16); |
| 261 | try testing::expect(syms[1].addr == 0x80002008); |
| 262 | try testing::expectBytesEq(&rw[..], &[97, 0, 0, 0, 0, 0, 0, 0, 98, 0, 0, 0, 0, 0, 0, 0]); |
| 263 | } |