lib/std/lang/il/binary/tests.rad 13.4 KiB raw
1
//! Binary RIL encoding fixtures.
2
3
use std::testing;
4
use std::lang::il;
5
use std::lang::il::binary::writer;
6
use std::lang::il::binary;
7
use std::lang::il::binary::reader;
8
use std::lang::alloc;
9
10
/// Decode arena backing storage. Tests reset it before each use.
11
static MEMORY: [u8; 512] = [0; 512];
12
13
/// Check little-endian encoding for every integer width.
14
@test unsafe fn integers() throws (testing::TestError) {
15
    let mut buffer: [u8; 15] = [0; 15];
16
    let mut out = writer::new(&mut buffer[..], &[]);
17
    try writer::integer(&mut out, 0x12, 1) catch { throw testing::TestError::Failed; };
18
    try writer::integer(&mut out, 0x3456, 2) catch { throw testing::TestError::Failed; };
19
    try writer::integer(&mut out, 0x789abcde, 4) catch { throw testing::TestError::Failed; };
20
    try writer::integer(&mut out, 0x0123456789abcdef, 8) catch { throw testing::TestError::Failed; };
21
    try testing::expectBytesEq(&buffer[..], &[0x12, 0x56, 0x34, 0xde, 0xbc, 0x9a, 0x78, 0xef, 0xcd,
22
        0xab, 0x89, 0x67, 0x45, 0x23, 0x01]);
23
}
24
25
/// Compare one instruction with its fixed wire representation.
26
unsafe fn instruction(item: il::Instr, expected: &[u8]) throws (testing::TestError) {
27
    let mut buffer: [u8; 256] = [0; 256];
28
    let mut out = writer::new(&mut buffer[..], &["data", "fn"]);
29
    try writer::instr(&mut out, item) catch { throw testing::TestError::Failed; };
30
    try testing::expectBytesEq(&buffer[..out.offset], expected);
31
    for capacity in 0..expected.len {
32
        let mut short = writer::new(&mut buffer[..capacity], &["data", "fn"]);
33
        let mut failed = false;
34
        try writer::instr(&mut short, item) catch err {
35
            try testing::expect(err == binary::Error::Capacity);
36
            set failed = true;
37
        };
38
        try testing::expect(failed);
39
        try testing::expect(short.offset <= capacity);
40
    }
41
    let memory = &mut MEMORY[..512];
42
    let mut arena = alloc::new(&mut memory[..]);
43
    let mut input = reader::new(expected, &mut arena, &["data", "fn"]);
44
    set input.registers = 16;
45
    set input.blocks = 4;
46
    let decoded = try reader::instr(&mut input) catch { throw testing::TestError::Failed; };
47
    try testing::expect(input.offset == expected.len);
48
    set out.offset = 0;
49
    try writer::instr(&mut out, decoded) catch { throw testing::TestError::Failed; };
50
    try testing::expectBytesEq(&buffer[..out.offset], expected);
51
    for length in 0..expected.len {
52
        alloc::reset(&mut arena);
53
        set input = reader::new(&expected[..length], &mut arena, &["data", "fn"]);
54
        set input.registers = 16;
55
        set input.blocks = 4;
56
        let mut failed = false;
57
        try reader::instr(&mut input) catch err {
58
            try testing::expect(err == binary::Error::Truncated);
59
            set failed = true;
60
        };
61
        try testing::expect(failed);
62
    }
63
64
}
65
66
/// Check all instruction tags and their field order.
67
@test unsafe fn instructions() throws (testing::TestError) {
68
    let mut args: [il::Val; 1] = [il::Val::Undef];
69
    let mut cases: [il::SwitchCase; 1] = [il::SwitchCase {
70
        value: -1, target: 2, args: &mut args[..],
71
    }];
72
    try instruction(il::Instr::Reserve {
73
        dst: il::Reg { n: 1 }, size: il::Val::Undef, alignment: 16,
74
    }, &[0, 1, 0, 0, 0, 4, 16, 0, 0, 0]);
75
    try instruction(il::Instr::Load {
76
        typ: il::Type::W8, dst: il::Reg { n: 1 }, src: il::Reg { n: 2 }, offset: -1,
77
    }, &[1, 1, 1, 0, 0, 0, 2, 0, 0, 0, 255, 255, 255, 255]);
78
    try instruction(il::Instr::Sload {
79
        typ: il::Type::W16, dst: il::Reg { n: 1 }, src: il::Reg { n: 2 }, offset: 3,
80
    }, &[2, 2, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0]);
81
    try instruction(il::Instr::Store {
82
        typ: il::Type::W32, src: il::Val::Undef, dst: il::Reg { n: 2 }, offset: 3,
83
    }, &[3, 4, 4, 2, 0, 0, 0, 3, 0, 0, 0]);
84
    try instruction(il::Instr::Blit {
85
        dst: il::Reg { n: 1 }, src: il::Reg { n: 2 }, size: il::Val::Undef,
86
    }, &[4, 1, 0, 0, 0, 2, 0, 0, 0, 4]);
87
    try instruction(il::Instr::Copy {
88
        dst: il::Reg { n: 1 }, val: il::Val::Undef,
89
    }, &[5, 1, 0, 0, 0, 4]);
90
    try instruction(il::Instr::BinOp {
91
        op: il::BinOp::Add, typ: il::Type::W64, dst: il::Reg { n: 1 }, a: il::Val::Undef,
92
        b: il::Val::Undef,
93
    }, &[6, 0, 8, 1, 0, 0, 0, 4, 4]);
94
    try instruction(il::Instr::UnOp {
95
        op: il::UnOp::Neg, typ: il::Type::W64, dst: il::Reg { n: 1 }, a: il::Val::Undef,
96
    }, &[7, 0, 8, 1, 0, 0, 0, 4]);
97
    try instruction(il::Instr::Zext {
98
        typ: il::Type::W8, dst: il::Reg { n: 1 }, val: il::Val::Undef,
99
    }, &[8, 1, 1, 0, 0, 0, 4]);
100
    try instruction(il::Instr::Sext {
101
        typ: il::Type::W16, dst: il::Reg { n: 1 }, val: il::Val::Undef,
102
    }, &[9, 2, 1, 0, 0, 0, 4]);
103
    try instruction(il::Instr::Call {
104
        retTy: il::Type::W64, dst: nil, func: il::Val::FnAddr("fn"), args: &[],
105
    }, &[10, 8, 0, 3, 1, 0, 0, 0, 0, 0, 0, 0]);
106
    try instruction(il::Instr::Call {
107
        retTy: il::Type::W64, dst: il::Reg { n: 2 }, func: il::Val::FnAddr("fn"), args: &args[..],
108
    }, &[10, 8, 1, 2, 0, 0, 0, 3, 1, 0, 0, 0, 1, 0, 0, 0, 4]);
109
    try instruction(il::Instr::Ret {
110
        val: nil,
111
    }, &[11, 0]);
112
    try instruction(il::Instr::Ret {
113
        val: il::Val::Undef,
114
    }, &[11, 1, 4]);
115
    try instruction(il::Instr::Jmp {
116
        target: 2, args: &mut args[..],
117
    }, &[12, 2, 0, 0, 0, 1, 0, 0, 0, 4]);
118
    try instruction(il::Instr::Br {
119
        op: il::CmpOp::Eq, typ: il::Type::W32, a: il::Val::Undef, b: il::Val::Undef, thenTarget: 1,
120
        thenArgs: &mut args[..], elseTarget: 2, elseArgs: &mut args[..],
121
    }, &[13, 0, 4, 4, 4, 1, 0, 0, 0, 1, 0, 0, 0, 4, 2, 0, 0, 0, 1, 0, 0, 0, 4]);
122
    try instruction(il::Instr::Switch {
123
        val: il::Val::Undef, defaultTarget: 1, defaultArgs: &mut args[..], cases: &mut cases[..],
124
    }, &[
125
        14, 4, 1, 0, 0, 0, 1, 0, 0, 0, 4, 1,
126
        0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 2,
127
        0, 0, 0, 1, 0, 0, 0, 4,
128
    ]);
129
    try instruction(il::Instr::Unreachable, &[15]);
130
    try instruction(il::Instr::Ecall {
131
        dst: il::Reg { n: 1 }, num: il::Val::Undef, a0: il::Val::Undef, a1: il::Val::Undef,
132
        a2: il::Val::Undef, a3: il::Val::Undef,
133
    }, &[16, 1, 0, 0, 0, 4, 4, 4, 4, 4]);
134
    try instruction(il::Instr::Ebreak, &[17]);
135
    try instruction(il::Instr::MemoryFence, &[18]);
136
    for typ in [il::Type::W8, il::Type::W16, il::Type::W32, il::Type::W64] {
137
        let mut read: [u8; 8] = [19, 0, 1, 0, 0, 0, 4, 4];
138
        let mut write: [u8; 5] = [20, 0, 4, 4, 4];
139
        set read[1] = il::typeSize(typ) as u8;
140
        set write[1] = il::typeSize(typ) as u8;
141
        try instruction(il::Instr::DeviceRead {
142
            typ, dst: il::Reg { n: 1 }, handle: il::Val::Undef, offset: il::Val::Undef,
143
        }, &read[..]);
144
        try instruction(il::Instr::DeviceWrite {
145
            typ, handle: il::Val::Undef, offset: il::Val::Undef, value: il::Val::Undef,
146
        }, &write[..]);
147
    }
148
}
149
150
/// Check every value tag and empty and nonempty sequences.
151
@test unsafe fn values() throws (testing::TestError) {
152
    let mut buffer: [u8; 64] = [0; 64];
153
    let mut out = writer::new(&mut buffer[..], &["data", "fn"]);
154
    try writer::values(&mut out, &[]) catch { throw testing::TestError::Failed; };
155
    try writer::values(&mut out, &[
156
        il::Val::Reg(il::Reg { n: 0x12345678 }),
157
        il::Val::Imm(-2), il::Val::DataSym("data"),
158
        il::Val::FnAddr("fn"), il::Val::Undef,
159
    ]) catch { throw testing::TestError::Failed; };
160
    try testing::expectBytesEq(&buffer[..out.offset], &[
161
        0, 0, 0, 0, 5, 0, 0, 0,
162
        0, 0x78, 0x56, 0x34, 0x12,
163
        1, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
164
        2, 0, 0, 0, 0, 3, 1, 0, 0, 0, 4,
165
    ]);
166
    let memory = &mut MEMORY[..256];
167
    let mut arena = alloc::new(&mut memory[..]);
168
    let mut input = reader::new(&buffer[..out.offset], &mut arena, &["data", "fn"]);
169
    set input.registers = 0x12345679;
170
    let empty = try reader::values(&mut input) catch { throw testing::TestError::Failed; };
171
    let decoded = try reader::values(&mut input) catch { throw testing::TestError::Failed; };
172
    try testing::expect(empty.len == 0);
173
    try testing::expect(decoded.len == 5);
174
    try testing::expect(input.offset == out.offset);
175
    let mut repeated: [u8; 64] = [0; 64];
176
    let mut copy = writer::new(&mut repeated[..], &["data", "fn"]);
177
    try writer::values(&mut copy, empty) catch { throw testing::TestError::Failed; };
178
    try writer::values(&mut copy, decoded) catch { throw testing::TestError::Failed; };
179
    try testing::expectBytesEq(&buffer[..out.offset], &repeated[..copy.offset]);
180
181
}
182
183
/// Check initializer bytes and repetition counts.
184
@test unsafe fn initializers() throws (testing::TestError) {
185
    let mut buffer: [u8; 128] = [0; 128];
186
    let mut out = writer::new(&mut buffer[..], &["data", "fn"]);
187
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W8,
188
        val: -1 }, count: 0 }) catch { throw testing::TestError::Failed; };
189
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W16,
190
        val: -2 }, count: 1 }) catch { throw testing::TestError::Failed; };
191
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W32,
192
        val: -3 }, count: 2 }) catch { throw testing::TestError::Failed; };
193
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W64,
194
        val: -4 }, count: 3 }) catch { throw testing::TestError::Failed; };
195
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Sym("data"), count: 4 })
196
        catch { throw testing::TestError::Failed; };
197
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Fn("fn"), count: 5 }) catch
198
        { throw testing::TestError::Failed; };
199
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Str("ab"), count: 6 }) catch
200
        { throw testing::TestError::Failed; };
201
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Str(""), count: 7 }) catch {
202
        throw testing::TestError::Failed; };
203
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Undef, count: 8 }) catch {
204
        throw testing::TestError::Failed; };
205
    try testing::expectBytesEq(&buffer[..out.offset], &[0, 1, 255, 0, 0, 0, 0, 0, 2, 254, 255, 1, 0,
206
        0, 0, 0, 4, 253, 255, 255, 255, 2, 0, 0, 0, 0, 8, 252, 255, 255, 255, 255, 255, 255, 255, 3,
207
        0, 0, 0, 1, 0, 0, 0, 0, 4, 0, 0, 0, 2, 1, 0, 0, 0, 5, 0, 0, 0, 3, 2, 0, 0, 0, 97, 98, 6, 0,
208
        0, 0, 3, 0, 0, 0, 0, 7, 0, 0, 0, 4, 8, 0, 0, 0]);
209
}
210
211
/// Reject invalid widths, insufficient storage, and absent symbols.
212
@test unsafe fn errors() throws (testing::TestError) {
213
    let mut buffer: [u8; 8] = [0; 8];
214
    let mut out = writer::new(&mut buffer[..], &[]);
215
    let mut failures: u32 = 0;
216
    try writer::integer(&mut out, 1, 3) catch err {
217
        try testing::expect(err == binary::Error::Invalid);
218
        set failures += 1;
219
    };
220
    try writer::symbol(&mut out, "absent") catch err {
221
        try testing::expect(err == binary::Error::Symbol);
222
        set failures += 1;
223
    };
224
    try testing::expect(out.offset == 0);
225
    try writer::integer(&mut out, 0, 8) catch { throw testing::TestError::Failed; };
226
    try writer::integer(&mut out, 1, 1) catch err {
227
        try testing::expect(err == binary::Error::Capacity);
228
        set failures += 1;
229
    };
230
    try testing::expect(failures == 3);
231
    try testing::expect(out.offset == 8);
232
}
233
234
/// Check operation tags independently of the native union representation.
235
@test unsafe fn operations() throws (testing::TestError) {
236
    let binaryOps = &[
237
        il::BinOp::Add, il::BinOp::Sub, il::BinOp::Mul,
238
        il::BinOp::Sdiv, il::BinOp::Udiv, il::BinOp::Srem,
239
        il::BinOp::Urem, il::BinOp::Eq, il::BinOp::Ne,
240
        il::BinOp::Slt, il::BinOp::Sge, il::BinOp::Ult,
241
        il::BinOp::Uge, il::BinOp::And, il::BinOp::Or,
242
        il::BinOp::Xor, il::BinOp::Shl, il::BinOp::Sshr,
243
        il::BinOp::Ushr,
244
    ];
245
    for op, tag in binaryOps {
246
        try instruction(il::Instr::BinOp {
247
            op, typ: il::Type::W64, dst: il::Reg { n: 1 },
248
            a: il::Val::Undef, b: il::Val::Undef,
249
        }, &[6, tag as u8, 8, 1, 0, 0, 0, 4, 4]);
250
    }
251
    for op, tag in &[il::UnOp::Neg, il::UnOp::Not] {
252
        try instruction(il::Instr::UnOp {
253
            op, typ: il::Type::W64, dst: il::Reg { n: 1 },
254
            a: il::Val::Undef,
255
        }, &[7, tag as u8, 8, 1, 0, 0, 0, 4]);
256
    }
257
    let mut empty: [il::Val; 0] = [];
258
    for op, tag in &[il::CmpOp::Eq, il::CmpOp::Ne, il::CmpOp::Slt, il::CmpOp::Ult] {
259
        try instruction(il::Instr::Br {
260
            op, typ: il::Type::W8, a: il::Val::Undef, b: il::Val::Undef,
261
            thenTarget: 1, thenArgs: &mut empty[..],
262
            elseTarget: 2, elseArgs: &mut empty[..],
263
        }, &[13, tag as u8, 1, 4, 4, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0]);
264
    }
265
}
266
267
/// Check byte-sequence bounds at every output size.
268
@test unsafe fn byteCapacity() throws (testing::TestError) {
269
    let mut buffer: [u8; 7] = [0; 7];
270
    for capacity in 0..7 {
271
        let mut out = writer::new(&mut buffer[..capacity], &[]);
272
        let mut failed = false;
273
        try writer::bytes(&mut out, "abc") catch err {
274
            try testing::expect(err == binary::Error::Capacity);
275
            set failed = true;
276
        };
277
        try testing::expect(failed);
278
        try testing::expect(out.offset <= capacity);
279
    }
280
    let mut out = writer::new(&mut buffer[..], &[]);
281
    try writer::bytes(&mut out, "abc") catch { throw testing::TestError::Failed; };
282
    try testing::expectBytesEq(&buffer[..], &[3, 0, 0, 0, 97, 98, 99]);
283
}