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