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