compiler/
kernel/
lib/
examples/
std/
arch/
char/
collections/
lang/
alloc/
ast/
gen/
il/
binary/
collect.rad
7.2 KiB
decodeTests.rad
14.9 KiB
program.rad
10.8 KiB
reader.rad
14.0 KiB
tests.rad
13.4 KiB
writer.rad
12.7 KiB
binary.rad
5.9 KiB
printer.rad
15.4 KiB
module/
parser/
resolver/
scanner/
alloc.rad
5.3 KiB
ast.rad
23.6 KiB
gen.rad
513 B
il.rad
17.0 KiB
lower.rad
278.5 KiB
module.rad
13.5 KiB
package.rad
1.3 KiB
parser.rad
79.6 KiB
resolver.rad
329.1 KiB
scanner.rad
17.6 KiB
sexpr.rad
6.4 KiB
strings.rad
2.2 KiB
types.rad
286 B
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
10.2 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
551 B
lib/std/lang/il/binary/tests.rad
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 | } |