compiler/
kernel/
lib/
examples/
std/
arch/
rv64/
asm/
image/
shared/
asm.rad
24.5 KiB
atomicTests.rad
5.0 KiB
atomics.rad
3.6 KiB
bounds.rad
8.7 KiB
decode.rad
15.2 KiB
emit.rad
33.2 KiB
encode.rad
22.0 KiB
image.rad
4.5 KiB
isel.rad
51.0 KiB
printer.rad
14.2 KiB
shared.rad
16.8 KiB
tests.rad
17.2 KiB
rv64.rad
17.1 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
9.2 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
662 B
lib/std/arch/rv64/bounds.rad
raw
| 1 | //! Recoverable backend capacity and relocation checks. |
| 2 | |
| 3 | use std::testing; |
| 4 | use std::io; |
| 5 | use std::lang::alloc; |
| 6 | use std::lang::il; |
| 7 | use std::lang::gen::data; |
| 8 | use std::lang::gen::labels; |
| 9 | use std::lang::gen::bitset; |
| 10 | use std::lang::gen::regalloc; |
| 11 | use std::collections::dict; |
| 12 | use super::emit; |
| 13 | use super::encode; |
| 14 | |
| 15 | /// Reusable emitter allocation storage. |
| 16 | static MEMORY: [u8; 16777216] = [0; 16777216]; |
| 17 | /// Function and liveness test storage. |
| 18 | static SCRATCH: [u8; 65536] = [0; 65536]; |
| 19 | |
| 20 | /// Dictionary storage for bounded map tests. |
| 21 | unsafe static ENTRIES: [dict::Entry; 4] = undefined; |
| 22 | |
| 23 | /// Build a non-debug generator with a fixed code address. |
| 24 | unsafe fn generator(arena: &mut alloc::Arena) -> super::Generator { |
| 25 | return try! super::beginProgram(super::ProgramOptions { |
| 26 | entryPatch: super::EntryPatch::None, debug: false, |
| 27 | placement: super::image::Placement::Physical { |
| 28 | code: 0x80000000, roData: 0x80001000, rwData: 0x80002000, entry: 0x80000000, |
| 29 | }, |
| 30 | }, arena); |
| 31 | } |
| 32 | |
| 33 | /// Exhaust each emitter storage class and ensure writes stop at the first error. |
| 34 | @test unsafe fn emissionCapacity() throws (testing::TestError) { |
| 35 | for kind in 0..8 { |
| 36 | let mut arena = alloc::new(&mut MEMORY[..]); |
| 37 | let mut e = try! emit::emitter(&mut arena, false); |
| 38 | match kind { |
| 39 | case 0 => { |
| 40 | set e.code = &mut e.code[..0]; |
| 41 | emit::emit(&mut e, encode::nop()); |
| 42 | }, |
| 43 | case 1 => { |
| 44 | set e.pendingBranchesLen = e.pendingBranches.len; |
| 45 | emit::recordBranch(&mut e, 0, emit::BranchKind::Jump); |
| 46 | }, |
| 47 | case 2 => { |
| 48 | set e.pendingCallsLen = e.pendingCalls.len; |
| 49 | emit::recordCall(&mut e, "p::call"); |
| 50 | }, |
| 51 | case 3 => { |
| 52 | set e.pendingJumpsLen = e.pendingJumps.len; |
| 53 | emit::recordJumpAt(&mut e, "p::jump", super::ZERO, 0); |
| 54 | }, |
| 55 | case 4 => { |
| 56 | set e.pendingAddrLoadsLen = e.pendingAddrLoads.len; |
| 57 | emit::recordDataAddrLoad(&mut e, "p::data", super::A0); |
| 58 | }, |
| 59 | case 5 => { |
| 60 | set e.funcsLen = e.funcs.len; |
| 61 | emit::recordFunc(&mut e, "p::call"); |
| 62 | }, |
| 63 | case 6 => { |
| 64 | let entries = &mut ENTRIES[..2]; |
| 65 | set e.labels.funcs = dict::init(&mut entries[..]); |
| 66 | emit::recordFuncOffset(&mut e, "p::first"); |
| 67 | emit::recordFuncOffset(&mut e, "p::second"); |
| 68 | }, |
| 69 | else => { |
| 70 | emit::recordSrcLoc(&mut e, il::SrcLoc { moduleId: 0, offset: 0 }); |
| 71 | }, |
| 72 | } |
| 73 | try testing::expect(e.error == super::Error::Capacity); |
| 74 | let count = e.codeLen; |
| 75 | emit::emit(&mut e, encode::ebreak()); |
| 76 | try testing::expect(e.codeLen == count); |
| 77 | let mut rejected = false; |
| 78 | try emit::check(&e) catch err { |
| 79 | try testing::expect(err == super::Error::Capacity); set rejected = true; |
| 80 | }; |
| 81 | try testing::expect(rejected); |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | /// Missing labels and long jumps return errors without corrupting instructions. |
| 86 | @test unsafe fn relocationFailures() throws (testing::TestError) { |
| 87 | let mut arena = alloc::new(&mut MEMORY[..]); |
| 88 | let mut e = try! emit::emitter(&mut arena, false); |
| 89 | emit::recordCall(&mut e, "missing"); |
| 90 | emit::patchCalls(&mut e); |
| 91 | try check(e.error == super::Error::Symbol, "missing function"); |
| 92 | alloc::reset(&mut arena); |
| 93 | set e = try! emit::emitter(&mut arena, false); |
| 94 | emit::recordBranch(&mut e, 0, emit::BranchKind::Jump); |
| 95 | labels::recordBlock(&mut e.labels, 0, 0x200000); |
| 96 | emit::patchLocalBranches(&mut e); |
| 97 | try check(e.error == super::Error::Relocation, "long branch"); |
| 98 | try check(e.code[0] == encode::nop(), "rejected branch unchanged"); |
| 99 | alloc::reset(&mut arena); |
| 100 | set e = try! emit::emitter(&mut arena, false); |
| 101 | let blockCount = e.labels.blockOffsets.len; |
| 102 | emit::recordBlock(&mut e, blockCount); |
| 103 | try check(e.error == super::Error::Capacity, "block capacity"); |
| 104 | } |
| 105 | |
| 106 | /// Arena setup and per-function failures restore their saved offsets for reuse. |
| 107 | @test unsafe fn arenaRecovery() throws (testing::TestError) { |
| 108 | let small = &mut SCRATCH[..64]; |
| 109 | let mut arena = alloc::new(&mut small[..]); |
| 110 | set arena.offset = 8; |
| 111 | let mut failed = false; |
| 112 | try super::beginProgram(super::ProgramOptions { |
| 113 | entryPatch: super::EntryPatch::None, debug: false, placement: super::image::Placement::Hosted, |
| 114 | }, &mut arena) catch err { |
| 115 | try check(err == super::Error::Allocation, "generator allocation"); set failed = true; |
| 116 | }; |
| 117 | try check(failed and arena.offset == 8, "generator arena restored"); |
| 118 | let mut code = alloc::new(&mut MEMORY[..]); |
| 119 | let mut gen = generator(&mut code); |
| 120 | let mut instructions = [ |
| 121 | il::Instr::Copy { dst: il::Reg { n: 0 }, val: il::Val::Imm(1) }, |
| 122 | il::Instr::Ret { val: il::Val::Reg(il::Reg { n: 0 }) }, |
| 123 | ]; |
| 124 | let func = il::Fn { |
| 125 | name: "p::one", params: &[], returnType: il::Type::W64, isExtern: false, isLeaf: true, |
| 126 | blocks: &[il::Block { label: "entry", params: &[], instrs: &mut instructions[..], locs: &[], preds: &[], loopDepth: 0 }], |
| 127 | }; |
| 128 | super::generateFunction(&mut gen, &func, &mut arena); |
| 129 | try check(gen.e.error == super::Error::Allocation and arena.offset == 8, "function arena restored"); |
| 130 | alloc::reset(&mut code); |
| 131 | set gen = generator(&mut code); |
| 132 | let mut scratch = alloc::new(&mut SCRATCH[..]); |
| 133 | set scratch.offset = 16; |
| 134 | super::generateFunction(&mut gen, &func, &mut scratch); |
| 135 | try check(gen.e.error == nil and gen.e.codeLen > 0 and scratch.offset == 16, "function retry"); |
| 136 | alloc::reset(&mut code); |
| 137 | set gen = generator(&mut code); |
| 138 | set instructions[0] = il::Instr::Copy { dst: il::Reg { n: 8192 }, val: il::Val::Imm(1) }; |
| 139 | super::generateFunction(&mut gen, &func, &mut scratch); |
| 140 | try check(gen.e.error == super::Error::Allocation and scratch.offset == 16, "SSA capacity"); |
| 141 | } |
| 142 | |
| 143 | /// Spill candidate storage fails explicitly when too many values are live. |
| 144 | @test unsafe fn registerStorage() throws (testing::TestError) { |
| 145 | let mut arena = alloc::new(&mut SCRATCH[..]); |
| 146 | use arena as bits in { |
| 147 | let liveSet = try! bitset::allocate(&bits, 257); |
| 148 | for i in 0..257 { |
| 149 | bitset::put(liveSet, i); |
| 150 | } |
| 151 | let live = regalloc::liveness::LiveInfo 'bits { |
| 152 | liveIn: &liveSet[..0], liveOut: &liveSet[..], |
| 153 | defs: &liveSet[..0], uses: &liveSet[..0], |
| 154 | words: bitset::wordsFor(257), blockCount: 1, maxReg: 257, |
| 155 | }; |
| 156 | let func = il::Fn { |
| 157 | name: "p::pressure", params: &[], returnType: il::Type::W64, isExtern: false, isLeaf: true, |
| 158 | blocks: &[il::Block { label: "entry", params: &[], instrs: &mut [], locs: &[], preds: &[], loopDepth: 0 }], |
| 159 | }; |
| 160 | let mut failed = false; |
| 161 | try regalloc::spill::analyze(&func, &live, 23, 11, 8, &bits) catch { |
| 162 | set failed = true; |
| 163 | }; |
| 164 | try testing::expect(failed); |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | /// Data output and symbol maps reject insufficient or ambiguous storage. |
| 169 | @test unsafe fn dataStorage() throws (testing::TestError) { |
| 170 | let mut arena = alloc::new(&mut MEMORY[..]); |
| 171 | let e = try! emit::emitter(&mut arena, false); |
| 172 | let syms = &[data::DataSym { name: "p::data", addr: 0x80002000 }]; |
| 173 | let entries = &mut ENTRIES[..2]; |
| 174 | let map = try! data::buildMap(syms, &mut entries[..]); |
| 175 | let items = &[il::Data { |
| 176 | name: "p::data", size: 8, alignment: 8, readOnly: false, isZeroInit: false, |
| 177 | values: &[il::DataValue { item: il::DataItem::Val { typ: il::Type::W64, val: 1 }, count: 1 }], |
| 178 | }]; |
| 179 | let mut bytes: [u8; 8] = [255; 8]; |
| 180 | let mut failed: u32 = 0; |
| 181 | try data::emitSection(items, &map, &e.labels, 0x80000000, &mut bytes[..7], false) catch err { |
| 182 | try testing::expect(err == data::Error::Capacity); set failed += 1; |
| 183 | }; |
| 184 | try data::buildMap(syms, &mut entries[..1]) catch err { |
| 185 | try testing::expect(err == data::Error::Capacity); set failed += 1; |
| 186 | }; |
| 187 | let larger = &mut ENTRIES[..4]; |
| 188 | unsafe static duplicate: [data::DataSym; 2] = undefined; |
| 189 | set duplicate = [syms[0], syms[0]]; |
| 190 | try data::buildMap(&duplicate[..], &mut larger[..]) catch err { |
| 191 | try testing::expect(err == data::Error::Symbol); set failed += 1; |
| 192 | }; |
| 193 | try testing::expect(failed == 3); |
| 194 | let length = try data::emitSection(items, &map, &e.labels, 0x80000000, &mut bytes[..], false) |
| 195 | catch { |
| 196 | throw testing::TestError::Failed; |
| 197 | }; |
| 198 | try testing::expect(length == 8 and bytes[0] == 1); |
| 199 | } |
| 200 | |
| 201 | /// Name the failed invariant before returning to the test runner. |
| 202 | fn check(condition: bool, name: *[u8]) throws (testing::TestError) { |
| 203 | if not condition { |
| 204 | io::printLn(name); |
| 205 | throw testing::TestError::Failed; |
| 206 | } |
| 207 | } |