lib/std/arch/rv64/bounds.rad 8.5 KiB 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 => { set e.code = &mut e.code[..0]; emit::emit(&mut e, encode::nop()); },
40
            case 1 => {
41
                set e.pendingBranches.len = 0; set e.pendingBranches.cap = 0;
42
                emit::recordBranch(&mut e, 0, emit::BranchKind::Jump);
43
            },
44
            case 2 => {
45
                set e.pendingCalls.len = 0; set e.pendingCalls.cap = 0;
46
                emit::recordCall(&mut e, "p::call");
47
            },
48
            case 3 => {
49
                set e.pendingJumps.len = 0; set e.pendingJumps.cap = 0;
50
                emit::recordJumpAt(&mut e, "p::jump", super::ZERO, 0);
51
            },
52
            case 4 => {
53
                set e.pendingAddrLoads.len = 0; set e.pendingAddrLoads.cap = 0;
54
                emit::recordDataAddrLoad(&mut e, "p::data", super::A0);
55
            },
56
            case 5 => {
57
                set e.funcs.len = 0; set e.funcs.cap = 0;
58
                emit::recordFunc(&mut e, "p::call");
59
            },
60
            case 6 => {
61
                let entries = &mut ENTRIES[..2];
62
                set e.labels.funcs = dict::init(&mut entries[..]);
63
                emit::recordFuncOffset(&mut e, "p::first");
64
                emit::recordFuncOffset(&mut e, "p::second");
65
            },
66
            else => { emit::recordSrcLoc(&mut e, il::SrcLoc { moduleId: 0, offset: 0 }); },
67
        }
68
        try testing::expect(e.error == super::Error::Capacity);
69
        let count = e.codeLen;
70
        emit::emit(&mut e, encode::ebreak());
71
        try testing::expect(e.codeLen == count);
72
        let mut rejected = false;
73
        try emit::check(&e) catch err {
74
            try testing::expect(err == super::Error::Capacity); set rejected = true;
75
        };
76
        try testing::expect(rejected);
77
    }
78
}
79
80
/// Missing labels and long jumps return errors without corrupting instructions.
81
@test unsafe fn relocationFailures() throws (testing::TestError) {
82
    let mut arena = alloc::new(&mut MEMORY[..]);
83
    let mut e = try! emit::emitter(&mut arena, false);
84
    emit::recordCall(&mut e, "missing");
85
    emit::patchCalls(&mut e);
86
    try check(e.error == super::Error::Symbol, "missing function");
87
    alloc::reset(&mut arena);
88
    set e = try! emit::emitter(&mut arena, false);
89
    emit::recordBranch(&mut e, 0, emit::BranchKind::Jump);
90
    labels::recordBlock(&mut e.labels, 0, 0x200000);
91
    emit::patchLocalBranches(&mut e);
92
    try check(e.error == super::Error::Relocation, "long branch");
93
    try check(e.code[0] == encode::nop(), "rejected branch unchanged");
94
    alloc::reset(&mut arena);
95
    set e = try! emit::emitter(&mut arena, false);
96
    let blockCount = e.labels.blockOffsets.len;
97
    emit::recordBlock(&mut e, blockCount);
98
    try check(e.error == super::Error::Capacity, "block capacity");
99
}
100
101
/// Arena setup and per-function failures restore their saved offsets for reuse.
102
@test unsafe fn arenaRecovery() throws (testing::TestError) {
103
    let small = &mut SCRATCH[..64];
104
    let mut arena = alloc::new(&mut small[..]);
105
    set arena.offset = 8;
106
    let mut failed = false;
107
    try super::beginProgram(super::ProgramOptions {
108
        entryPatch: super::EntryPatch::None, debug: false, placement: super::image::Placement::Hosted,
109
    }, &mut arena) catch err {
110
        try check(err == super::Error::Allocation, "generator allocation"); set failed = true;
111
    };
112
    try check(failed and arena.offset == 8, "generator arena restored");
113
    let mut code = alloc::new(&mut MEMORY[..]);
114
    let mut gen = generator(&mut code);
115
    let mut instructions = [
116
        il::Instr::Copy { dst: il::Reg { n: 0 }, val: il::Val::Imm(1) },
117
        il::Instr::Ret { val: il::Val::Reg(il::Reg { n: 0 }) },
118
    ];
119
    let func = il::Fn {
120
        name: "p::one", params: &[], returnType: il::Type::W64, isExtern: false, isLeaf: true,
121
        blocks: &[il::Block { label: "entry", params: &[], instrs: &mut instructions[..], locs: &[], preds: &[], loopDepth: 0 }],
122
    };
123
    super::generateFunction(&mut gen, &func, &mut arena);
124
    try check(gen.e.error == super::Error::Allocation and arena.offset == 8, "function arena restored");
125
    alloc::reset(&mut code);
126
    set gen = generator(&mut code);
127
    let mut scratch = alloc::new(&mut SCRATCH[..]);
128
    set scratch.offset = 16;
129
    super::generateFunction(&mut gen, &func, &mut scratch);
130
    try check(gen.e.error == nil and gen.e.codeLen > 0 and scratch.offset == 16, "function retry");
131
    alloc::reset(&mut code);
132
    set gen = generator(&mut code);
133
    set instructions[0] = il::Instr::Copy { dst: il::Reg { n: 8192 }, val: il::Val::Imm(1) };
134
    super::generateFunction(&mut gen, &func, &mut scratch);
135
    try check(gen.e.error == super::Error::Allocation and scratch.offset == 16, "SSA capacity");
136
}
137
138
/// Spill candidate storage fails explicitly when too many values are live.
139
@test unsafe fn registerStorage() throws (testing::TestError) {
140
    let mut arena = alloc::new(&mut SCRATCH[..]);
141
    let mut liveSet = try! bitset::allocate(&mut arena, 257);
142
    for i in 0..257 { bitset::put(&mut liveSet, i); }
143
    let mut out = [liveSet];
144
    let live = regalloc::liveness::LiveInfo {
145
        liveIn: &mut [], liveOut: &mut out[..], defs: &mut [], uses: &mut [], blockCount: 1, maxReg: 257,
146
    };
147
    let func = il::Fn {
148
        name: "p::pressure", params: &[], returnType: il::Type::W64, isExtern: false, isLeaf: true,
149
        blocks: &[il::Block { label: "entry", params: &[], instrs: &mut [], locs: &[], preds: &[], loopDepth: 0 }],
150
    };
151
    let mut failed = false;
152
    try regalloc::spill::analyze(&func, &live, 23, 11, 8, &mut arena) catch {
153
        set failed = true;
154
    };
155
    try testing::expect(failed);
156
}
157
158
/// Data output and symbol maps reject insufficient or ambiguous storage.
159
@test unsafe fn dataStorage() throws (testing::TestError) {
160
    let mut arena = alloc::new(&mut MEMORY[..]);
161
    let e = try! emit::emitter(&mut arena, false);
162
    let syms = &[data::DataSym { name: "p::data", addr: 0x80002000 }];
163
    let entries = &mut ENTRIES[..2];
164
    let map = try! data::buildMap(syms, &mut entries[..]);
165
    let items = &[il::Data {
166
        name: "p::data", size: 8, alignment: 8, readOnly: false, isZeroInit: false,
167
        values: &[il::DataValue { item: il::DataItem::Val { typ: il::Type::W64, val: 1 }, count: 1 }],
168
    }];
169
    let mut bytes: [u8; 8] = [255; 8];
170
    let mut failed: u32 = 0;
171
    try data::emitSection(items, &map, &e.labels, 0x80000000, &mut bytes[..7], false) catch err {
172
        try testing::expect(err == data::Error::Capacity); set failed += 1;
173
    };
174
    try data::buildMap(syms, &mut entries[..1]) catch err {
175
        try testing::expect(err == data::Error::Capacity); set failed += 1;
176
    };
177
    let larger = &mut ENTRIES[..4];
178
    unsafe static duplicate: [data::DataSym; 2] = undefined;
179
    set duplicate = [syms[0], syms[0]];
180
    try data::buildMap(&duplicate[..], &mut larger[..]) catch err {
181
        try testing::expect(err == data::Error::Symbol); set failed += 1;
182
    };
183
    try testing::expect(failed == 3);
184
    let length = try data::emitSection(items, &map, &e.labels, 0x80000000, &mut bytes[..], false)
185
        catch { throw testing::TestError::Failed; };
186
    try testing::expect(length == 8 and bytes[0] == 1);
187
}
188
189
/// Name the failed invariant before returning to the test runner.
190
fn check(condition: bool, name: *[u8]) throws (testing::TestError) {
191
    if not condition { io::printLn(name); throw testing::TestError::Failed; }
192
}