compiler/
lib/
examples/
std/
arch/
char/
collections/
lang/
alloc/
ast/
gen/
il/
binary/
collect.rad
9.5 KiB
decodeTests.rad
32.7 KiB
program.rad
15.3 KiB
reader.rad
15.4 KiB
tests.rad
26.5 KiB
writer.rad
13.9 KiB
binary.rad
5.9 KiB
printer.rad
16.0 KiB
tests.rad
14.7 KiB
module/
parser/
resolver/
scanner/
alloc.rad
7.1 KiB
ast.rad
26.7 KiB
gen.rad
513 B
il.rad
20.3 KiB
lower.rad
315.0 KiB
module.rad
14.9 KiB
package.rad
1.3 KiB
parser.rad
91.2 KiB
resolver.rad
452.9 KiB
scanner.rad
17.9 KiB
sexpr.rad
6.7 KiB
strings.rad
2.2 KiB
types.rad
1.6 KiB
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
5.4 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
662 B
lib/std/lang/il/binary/reader.rad
raw
| 1 | //! Checked binary RIL decoding into caller-owned arena storage. |
| 2 | //! Callers discard partial results and restore the arena after a failed decode. |
| 3 | |
| 4 | use std::lang::il; |
| 5 | use std::lang::il::binary; |
| 6 | use std::lang::alloc; |
| 7 | |
| 8 | /// Input cursor and reconstruction storage. |
| 9 | /// Input bytes and the symbol table are borrowed for the reader's region. |
| 10 | /// Allocation requires valid raw storage. |
| 11 | export record Reader: 'input + Copy { |
| 12 | /// Encoded bytes. |
| 13 | bytes: &'input [u8], |
| 14 | /// Number of bytes consumed. |
| 15 | offset: u32, |
| 16 | /// Storage for decoded sequences and byte strings. |
| 17 | arena: *unsafe mut alloc::Arena, |
| 18 | /// Symbol names in wire-index order. |
| 19 | symbols: &'input [*[u8]], |
| 20 | /// Exclusive bound for SSA register numbers. |
| 21 | registers: u32, |
| 22 | /// Number of blocks in the current function. |
| 23 | blocks: u32, |
| 24 | } |
| 25 | |
| 26 | /// Create a cursor. Set function bounds before reading instructions. |
| 27 | export fn new 'input (bytes: &'input [u8], arena: *unsafe mut alloc::Arena, symbols: &'input [*[u8]]) -> Reader 'input { |
| 28 | return Reader 'input { bytes, offset: 0, arena, symbols, registers: 0, blocks: 0 }; |
| 29 | } |
| 30 | |
| 31 | /// Read an unsigned integer with width 1, 2, 4, or 8. |
| 32 | export fn integer 'input (input: &mut Reader 'input, width: u32) -> u64 throws (binary::Error) { |
| 33 | if width <> 1 and width <> 2 and width <> 4 and width <> 8 { |
| 34 | throw binary::Error::Invalid; |
| 35 | } |
| 36 | if input.offset > input.bytes.len or width > input.bytes.len - input.offset { |
| 37 | throw binary::Error::Truncated; |
| 38 | } |
| 39 | let mut result: u64 = 0; |
| 40 | for i in 0..width { |
| 41 | set result |= (input.bytes[input.offset + i] as u64) << (i * 8) as u64; |
| 42 | } |
| 43 | set input.offset += width; |
| 44 | return result; |
| 45 | } |
| 46 | |
| 47 | /// Read a zero-or-one optional-field or boolean marker. |
| 48 | export fn flag 'input (input: &mut Reader 'input) -> bool throws (binary::Error) { |
| 49 | let n = try integer(input, 1); |
| 50 | if n > 1 { |
| 51 | throw binary::Error::Invalid; |
| 52 | } |
| 53 | return n == 1; |
| 54 | } |
| 55 | |
| 56 | /// Read a count and check the minimum required input bytes before allocation. |
| 57 | export fn count 'input (input: &mut Reader 'input, minimum: u32) -> u32 throws (binary::Error) { |
| 58 | assert minimum > 0; |
| 59 | let n = try integer(input, 4) as u32; |
| 60 | if n > (input.bytes.len - input.offset) / minimum { |
| 61 | throw binary::Error::Truncated; |
| 62 | } |
| 63 | return n; |
| 64 | } |
| 65 | |
| 66 | /// Allocate a typed sequence after checking size and alignment arithmetic. |
| 67 | export unsafe fn storage 'input (input: &mut Reader 'input, size: u32, alignment: u32, count: u32) |
| 68 | -> *mut [opaque] throws (binary::Error) |
| 69 | { |
| 70 | assert size > 0 and alignment > 0 and (alignment & (alignment - 1)) == 0; |
| 71 | return try alloc::allocSlice(input.arena, size, alignment, count) catch { |
| 72 | throw binary::Error::Storage; |
| 73 | }; |
| 74 | } |
| 75 | |
| 76 | /// Read a length-prefixed byte string and copy it into the arena. |
| 77 | export unsafe fn bytes 'input (input: &mut Reader 'input) -> *[u8] throws (binary::Error) { |
| 78 | let n = try count(input, 1); |
| 79 | let result = try storage(input, @sizeOf(u8), @alignOf(u8), n) as *mut [u8]; |
| 80 | return fillBytes(input, result); |
| 81 | } |
| 82 | |
| 83 | /// Fill an owned byte buffer from input and advance past the copied bytes. |
| 84 | fn fillBytes 'input (input: &mut Reader 'input, result: *mut [u8]) -> *[u8] { |
| 85 | let n = result.len; |
| 86 | for i in 0..n { |
| 87 | set result[i] = input.bytes[input.offset + i]; |
| 88 | } |
| 89 | set input.offset += n; |
| 90 | return result; |
| 91 | } |
| 92 | |
| 93 | /// Resolve a checked symbol-table index. |
| 94 | export fn symbol 'input (input: &mut Reader 'input) -> *[u8] throws (binary::Error) { |
| 95 | let index = try integer(input, 4) as u32; |
| 96 | if index >= input.symbols.len { |
| 97 | throw binary::Error::Symbol; |
| 98 | } |
| 99 | return input.symbols[index]; |
| 100 | } |
| 101 | |
| 102 | /// Read an IL type from its byte width. |
| 103 | export fn typ 'input (input: &mut Reader 'input) -> il::Type throws (binary::Error) { |
| 104 | let width = try integer(input, 1); |
| 105 | match width { |
| 106 | case 1 => return il::Type::W8, |
| 107 | case 2 => return il::Type::W16, |
| 108 | case 4 => return il::Type::W32, |
| 109 | case 8 => return il::Type::W64, |
| 110 | else => throw binary::Error::Invalid, |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | /// Read a register index within the current function's bound. |
| 115 | export fn reg 'input (input: &mut Reader 'input) -> il::Reg throws (binary::Error) { |
| 116 | let n = try integer(input, 4) as u32; |
| 117 | if n >= input.registers { |
| 118 | throw binary::Error::Invalid; |
| 119 | } |
| 120 | return il::Reg { n }; |
| 121 | } |
| 122 | |
| 123 | /// Read a target block index within the current function. |
| 124 | export fn target 'input (input: &mut Reader 'input) -> u32 throws (binary::Error) { |
| 125 | let n = try integer(input, 4) as u32; |
| 126 | if n >= input.blocks { |
| 127 | throw binary::Error::Invalid; |
| 128 | } |
| 129 | return n; |
| 130 | } |
| 131 | |
| 132 | /// Read a tagged value with checked register and symbol indices. |
| 133 | export fn val 'input (input: &mut Reader 'input) -> il::Val throws (binary::Error) { |
| 134 | let tag = try integer(input, 1) as u8; |
| 135 | match tag { |
| 136 | case super::VALUE_REG => return il::Val::Reg(try reg(input)), |
| 137 | case super::VALUE_IMM => return il::Val::Imm(try integer(input, 8) as i64), |
| 138 | case super::VALUE_DATASYM => return il::Val::DataSym(try symbol(input)), |
| 139 | case super::VALUE_FNADDR => return il::Val::FnAddr(try symbol(input)), |
| 140 | case super::VALUE_UNDEF => return il::Val::Undef, |
| 141 | else => throw binary::Error::Invalid, |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | /// Read a counted sequence of values. |
| 146 | export unsafe fn values 'input (input: &mut Reader 'input) -> *unsafe mut [il::Val] throws (binary::Error) { |
| 147 | let n = try count(input, 1); |
| 148 | let result = try storage(input, @sizeOf(il::Val), @alignOf(il::Val), n) as *mut [il::Val]; |
| 149 | let initialized = try fillValues(input, result); |
| 150 | return (&mut initialized[..]) as *unsafe mut [il::Val]; |
| 151 | } |
| 152 | |
| 153 | /// Fill an owned operand table with checked tagged values in input order. |
| 154 | fn fillValues 'input (input: &mut Reader 'input, result: *mut [il::Val]) -> *mut [il::Val] throws (binary::Error) { |
| 155 | for i in 0..result.len { |
| 156 | set result[i] = try val(input); |
| 157 | } |
| 158 | return result; |
| 159 | } |
| 160 | |
| 161 | /// Read a checked bin operation tag. |
| 162 | fn binOp 'input (input: &mut Reader 'input) -> il::BinOp throws (binary::Error) { |
| 163 | let tag = try integer(input, 1) as u8; |
| 164 | match tag { |
| 165 | case super::BIN_ADD => return il::BinOp::Add, |
| 166 | case super::BIN_SUB => return il::BinOp::Sub, |
| 167 | case super::BIN_MUL => return il::BinOp::Mul, |
| 168 | case super::BIN_SDIV => return il::BinOp::Sdiv, |
| 169 | case super::BIN_UDIV => return il::BinOp::Udiv, |
| 170 | case super::BIN_SREM => return il::BinOp::Srem, |
| 171 | case super::BIN_UREM => return il::BinOp::Urem, |
| 172 | case super::BIN_EQ => return il::BinOp::Eq, |
| 173 | case super::BIN_NE => return il::BinOp::Ne, |
| 174 | case super::BIN_SLT => return il::BinOp::Slt, |
| 175 | case super::BIN_SGE => return il::BinOp::Sge, |
| 176 | case super::BIN_ULT => return il::BinOp::Ult, |
| 177 | case super::BIN_UGE => return il::BinOp::Uge, |
| 178 | case super::BIN_AND => return il::BinOp::And, |
| 179 | case super::BIN_OR => return il::BinOp::Or, |
| 180 | case super::BIN_XOR => return il::BinOp::Xor, |
| 181 | case super::BIN_SHL => return il::BinOp::Shl, |
| 182 | case super::BIN_SSHR => return il::BinOp::Sshr, |
| 183 | case super::BIN_USHR => return il::BinOp::Ushr, |
| 184 | else => throw binary::Error::Invalid, |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | /// Read a checked un operation tag. |
| 189 | fn unOp 'input (input: &mut Reader 'input) -> il::UnOp throws (binary::Error) { |
| 190 | let tag = try integer(input, 1) as u8; |
| 191 | match tag { |
| 192 | case super::UN_NEG => return il::UnOp::Neg, |
| 193 | case super::UN_NOT => return il::UnOp::Not, |
| 194 | else => throw binary::Error::Invalid, |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | /// Read a checked cmp operation tag. |
| 199 | fn cmpOp 'input (input: &mut Reader 'input) -> il::CmpOp throws (binary::Error) { |
| 200 | let tag = try integer(input, 1) as u8; |
| 201 | match tag { |
| 202 | case super::CMP_EQ => return il::CmpOp::Eq, |
| 203 | case super::CMP_NE => return il::CmpOp::Ne, |
| 204 | case super::CMP_SLT => return il::CmpOp::Slt, |
| 205 | case super::CMP_ULT => return il::CmpOp::Ult, |
| 206 | else => throw binary::Error::Invalid, |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | /// Read an instruction with inline operands. |
| 211 | fn fixedInstr 'input (input: &mut Reader 'input, tag: u8) -> il::Instr throws (binary::Error) { |
| 212 | match tag { |
| 213 | case super::INSTR_RESERVE => { |
| 214 | let vdst = try reg(input); |
| 215 | let vsize = try val(input); |
| 216 | let valignment = try integer(input, 4) as u32; |
| 217 | return il::Instr::Reserve { dst: vdst, size: vsize, alignment: valignment }; |
| 218 | }, |
| 219 | case super::INSTR_LOAD, super::INSTR_SLOAD => { |
| 220 | let vtyp = try typ(input); |
| 221 | let vdst = try reg(input); |
| 222 | let vsrc = try reg(input); |
| 223 | let voffset = try integer(input, 4) as i32; |
| 224 | if tag == super::INSTR_SLOAD { |
| 225 | return il::Instr::Sload { typ: vtyp, dst: vdst, src: vsrc, offset: voffset }; |
| 226 | } |
| 227 | return il::Instr::Load { typ: vtyp, dst: vdst, src: vsrc, offset: voffset }; |
| 228 | }, |
| 229 | case super::INSTR_STORE => { |
| 230 | let vtyp = try typ(input); |
| 231 | let vsrc = try val(input); |
| 232 | let vdst = try reg(input); |
| 233 | let voffset = try integer(input, 4) as i32; |
| 234 | return il::Instr::Store { typ: vtyp, src: vsrc, dst: vdst, offset: voffset }; |
| 235 | }, |
| 236 | case super::INSTR_BLIT => { |
| 237 | let vdst = try reg(input); |
| 238 | let vsrc = try reg(input); |
| 239 | let vsize = try val(input); |
| 240 | return il::Instr::Blit { dst: vdst, src: vsrc, size: vsize }; |
| 241 | }, |
| 242 | case super::INSTR_COPY => { |
| 243 | let vdst = try reg(input); |
| 244 | let vval = try val(input); |
| 245 | return il::Instr::Copy { dst: vdst, val: vval }; |
| 246 | }, |
| 247 | case super::INSTR_BINOP => { |
| 248 | let vop = try binOp(input); |
| 249 | let vtyp = try typ(input); |
| 250 | let vdst = try reg(input); |
| 251 | let va = try val(input); |
| 252 | let vb = try val(input); |
| 253 | return il::Instr::BinOp { op: vop, typ: vtyp, dst: vdst, a: va, b: vb }; |
| 254 | }, |
| 255 | case super::INSTR_UNOP => { |
| 256 | let vop = try unOp(input); |
| 257 | let vtyp = try typ(input); |
| 258 | let vdst = try reg(input); |
| 259 | let va = try val(input); |
| 260 | return il::Instr::UnOp { op: vop, typ: vtyp, dst: vdst, a: va }; |
| 261 | }, |
| 262 | case super::INSTR_ZEXT, super::INSTR_SEXT => { |
| 263 | let vtyp = try typ(input); |
| 264 | let vdst = try reg(input); |
| 265 | let vval = try val(input); |
| 266 | if tag == super::INSTR_SEXT { |
| 267 | return il::Instr::Sext { typ: vtyp, dst: vdst, val: vval }; |
| 268 | } |
| 269 | return il::Instr::Zext { typ: vtyp, dst: vdst, val: vval }; |
| 270 | }, |
| 271 | case super::INSTR_RET => { |
| 272 | let mut vval: ?il::Val = nil; |
| 273 | if try flag(input) { |
| 274 | set vval = try val(input); |
| 275 | } |
| 276 | return il::Instr::Ret { val: vval }; |
| 277 | }, |
| 278 | case super::INSTR_UNREACHABLE => { |
| 279 | return il::Instr::Unreachable; |
| 280 | }, |
| 281 | case super::INSTR_ECALL => { |
| 282 | let vdst = try reg(input); |
| 283 | let vnum = try val(input); |
| 284 | let va0 = try val(input); |
| 285 | let va1 = try val(input); |
| 286 | let va2 = try val(input); |
| 287 | let va3 = try val(input); |
| 288 | return il::Instr::Ecall { dst: vdst, num: vnum, a0: va0, a1: va1, a2: va2, a3: va3 }; |
| 289 | }, |
| 290 | case super::INSTR_DEVICE_READ => { |
| 291 | let t = try typ(input); |
| 292 | let dst = try reg(input); |
| 293 | let handle = try val(input); let offset = try val(input); |
| 294 | return il::Instr::DeviceRead { typ: t, dst, handle, offset }; |
| 295 | }, |
| 296 | case super::INSTR_DEVICE_WRITE => { |
| 297 | let t = try typ(input); |
| 298 | let handle = try val(input); let offset = try val(input); let value = try val(input); |
| 299 | return il::Instr::DeviceWrite { typ: t, handle, offset, value }; |
| 300 | }, |
| 301 | case super::INSTR_EBREAK => { |
| 302 | return il::Instr::Ebreak; |
| 303 | }, |
| 304 | case super::INSTR_MEMORYFENCE => { |
| 305 | return il::Instr::MemoryFence; |
| 306 | }, |
| 307 | else => throw binary::Error::Invalid, |
| 308 | } |
| 309 | } |
| 310 | |
| 311 | /// Read one instruction and reconstruct its operand sequences. |
| 312 | export unsafe fn instr 'input (input: &mut Reader 'input) -> il::Instr throws (binary::Error) { |
| 313 | let tag = try integer(input, 1) as u8; |
| 314 | match tag { |
| 315 | case super::INSTR_CALL => { |
| 316 | let vretTy = try typ(input); |
| 317 | let mut vdst: ?il::Reg = nil; |
| 318 | if try flag(input) { |
| 319 | set vdst = try reg(input); |
| 320 | } |
| 321 | let vfunc = try val(input); |
| 322 | let vargs = try values(input); |
| 323 | return il::Instr::Call { retTy: vretTy, dst: vdst, func: vfunc, args: vargs }; |
| 324 | }, |
| 325 | case super::INSTR_JMP => { |
| 326 | let vtarget = try target(input); |
| 327 | let vargs = try values(input); |
| 328 | return il::Instr::Jmp { target: vtarget, args: vargs }; |
| 329 | }, |
| 330 | case super::INSTR_BR => { |
| 331 | let vop = try cmpOp(input); |
| 332 | let vtyp = try typ(input); |
| 333 | let va = try val(input); |
| 334 | let vb = try val(input); |
| 335 | let vthenTarget = try target(input); |
| 336 | let vthenArgs = try values(input); |
| 337 | let velseTarget = try target(input); |
| 338 | let velseArgs = try values(input); |
| 339 | return il::Instr::Br { op: vop, typ: vtyp, a: va, b: vb, thenTarget: vthenTarget, thenArgs: vthenArgs, elseTarget: velseTarget, elseArgs: velseArgs }; |
| 340 | }, |
| 341 | case super::INSTR_SWITCH => { |
| 342 | let vval = try val(input); |
| 343 | let vdefaultTarget = try target(input); |
| 344 | let vdefaultArgs = try values(input); |
| 345 | let n = try count(input, 16); |
| 346 | let caseStorage = try storage(input, @sizeOf(il::SwitchCase), @alignOf(il::SwitchCase), n) |
| 347 | as *mut [il::SwitchCase]; |
| 348 | let vcases = try fillCases(input, caseStorage); |
| 349 | return il::Instr::Switch { val: vval, defaultTarget: vdefaultTarget, defaultArgs: vdefaultArgs, cases: (&mut vcases[..]) as *unsafe mut [il::SwitchCase] }; |
| 350 | }, |
| 351 | else => return try fixedInstr(input, tag), |
| 352 | } |
| 353 | } |
| 354 | |
| 355 | /// Fill owned switch cases with signed values and checked block targets. |
| 356 | fn fillCases 'input (input: &mut Reader 'input, cases: *mut [il::SwitchCase]) -> *mut [il::SwitchCase] throws (binary::Error) { |
| 357 | for i in 0..cases.len { |
| 358 | let value = try integer(input, 8) as i64; |
| 359 | let block = try target(input); |
| 360 | unsafe { |
| 361 | let args = try values(input); |
| 362 | set cases[i] = il::SwitchCase { value, target: block, args }; |
| 363 | } |
| 364 | } |
| 365 | return cases; |
| 366 | } |
| 367 | |
| 368 | /// Read an initializer with a repetition count. |
| 369 | export unsafe fn dataValue 'input (input: &mut Reader 'input) -> il::DataValue throws (binary::Error) { |
| 370 | let tag = try integer(input, 1) as u8; |
| 371 | let item = il::DataItem::Str(try bytes(input)) if tag == super::DATA_STR |
| 372 | else try fixedDataItem(input, tag); |
| 373 | let n = try integer(input, 4) as u32; |
| 374 | return il::DataValue { item, count: n }; |
| 375 | } |
| 376 | |
| 377 | /// Decode a data item whose payload needs no allocation. |
| 378 | fn fixedDataItem 'input (input: &mut Reader 'input, tag: u8) -> il::DataItem throws (binary::Error) { |
| 379 | match tag { |
| 380 | case super::DATA_VAL => { |
| 381 | let t = try typ(input); |
| 382 | let n = try integer(input, il::typeSize(t)); |
| 383 | return il::DataItem::Val { typ: t, val: n as i64 }; |
| 384 | }, |
| 385 | case super::DATA_SYM => { |
| 386 | return il::DataItem::Sym(try symbol(input)); |
| 387 | }, |
| 388 | case super::DATA_FN => { |
| 389 | return il::DataItem::Fn(try symbol(input)); |
| 390 | }, |
| 391 | case super::DATA_UNDEF => { |
| 392 | return il::DataItem::Undef; |
| 393 | }, |
| 394 | else => throw binary::Error::Invalid, |
| 395 | } |
| 396 | } |