lib/std/arch/rv64/bounds.rad 8.7 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 => {
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
}