lib/std/lang/il/binary/reader.rad 15.4 KiB 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
}