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/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 => { set item = il::DataItem::Sym(try symbol(input)); }, |
| 351 | case super::DATA_FN => { set item = il::DataItem::Fn(try symbol(input)); }, |
| 352 | case super::DATA_STR => { set item = il::DataItem::Str(try bytes(input)); }, |
| 353 | case super::DATA_UNDEF => { set item = il::DataItem::Undef; }, |
| 354 | else => throw binary::Error::Invalid, |
| 355 | } |
| 356 | let n = try integer(input, 4) as u32; |
| 357 | return il::DataValue { item, count: n }; |
| 358 | } |