lib/std/lang/il/binary/reader.rad 17.2 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 bytes and the symbol table are borrowed for the reader's region.
9
/// Allocation requires valid raw storage.
10
export record Reader: 'input + Copy {
11
    /// Encoded bytes.
12
    bytes: &'input [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: &'input [*[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 fn new 'input (bytes: &'input [u8], arena: *unsafe mut alloc::Arena, symbols: &'input [*[u8]]) -> Reader 'input {
27
    return Reader 'input { bytes, offset: 0, arena, symbols, registers: 0, blocks: 0 };
28
}
29
30
/// Read an unsigned integer with width 1, 2, 4, or 8.
31
export fn integer 'input (input: &mut Reader 'input, 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 fn flag 'input (input: &mut Reader 'input) -> 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 fn count 'input (input: &mut Reader 'input, 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 (input: &mut Reader 'input, 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 (input: &mut Reader 'input) -> *[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
    return fillBytes(input, result);
80
}
81
82
/// Fill an owned byte buffer from input and advance past the copied bytes.
83
fn fillBytes 'input (input: &mut Reader 'input, result: *mut [u8]) -> *[u8] {
84
    let n = result.len;
85
    for i in 0..n {
86
        set result[i] = input.bytes[input.offset + i];
87
    }
88
    set input.offset += n;
89
    return result;
90
}
91
92
/// Resolve a checked symbol-table index.
93
export fn symbol 'input (input: &mut Reader 'input) -> *[u8] throws (binary::Error) {
94
    let index = try integer(input, 4) as u32;
95
    if index >= input.symbols.len {
96
        throw binary::Error::Symbol;
97
    }
98
    return input.symbols[index];
99
}
100
101
/// Read an IL type from its byte width.
102
export fn typ 'input (input: &mut Reader 'input) -> il::Type throws (binary::Error) {
103
    let width = try integer(input, 1);
104
    match width {
105
        case 1 => return il::Type::W8,
106
        case 2 => return il::Type::W16,
107
        case 4 => return il::Type::W32,
108
        case 8 => return il::Type::W64,
109
        else => throw binary::Error::Invalid,
110
    }
111
}
112
113
/// Read a register index within the current function's bound.
114
export fn reg 'input (input: &mut Reader 'input) -> il::Reg throws (binary::Error) {
115
    let n = try integer(input, 4) as u32;
116
    if n >= input.registers {
117
        throw binary::Error::Invalid;
118
    }
119
    return il::Reg { n };
120
}
121
122
/// Read a target block index within the current function.
123
export fn target 'input (input: &mut Reader 'input) -> u32 throws (binary::Error) {
124
    let n = try integer(input, 4) as u32;
125
    if n >= input.blocks {
126
        throw binary::Error::Invalid;
127
    }
128
    return n;
129
}
130
131
/// Read a tagged value with checked register and symbol indices.
132
export fn val 'input (input: &mut Reader 'input) -> il::Val throws (binary::Error) {
133
    let tag = try integer(input, 1) as u8;
134
    match tag {
135
        case super::VALUE_REG => return il::Val::Reg(try reg(input)),
136
        case super::VALUE_IMM => return il::Val::Imm(try integer(input, 8) as i64),
137
        case super::VALUE_DATASYM => return il::Val::DataSym(try symbol(input)),
138
        case super::VALUE_FNADDR => return il::Val::FnAddr(try symbol(input)),
139
        case super::VALUE_UNDEF => return il::Val::Undef,
140
        else => throw binary::Error::Invalid,
141
    }
142
}
143
144
/// Read a counted sequence of values.
145
export unsafe fn values 'input (input: &mut Reader 'input) -> *unsafe mut [il::Val] throws (binary::Error) {
146
    let n = try count(input, 1);
147
    let result = try newValues(input, n);
148
    let initialized = try fillValues(input, result);
149
    return (&mut initialized[..]) as *unsafe mut [il::Val];
150
}
151
152
/// Exact-capacity storage with a fully initialized operand prefix.
153
record ValueBuilder {
154
    /// Opaque storage reserved for the full operand count.
155
    storage: *mut [opaque],
156
    /// Number of initialized operands in the prefix.
157
    initialized: u32,
158
}
159
160
/// Reserve operand storage without exposing uninitialized typed elements.
161
unsafe fn newValues 'input (input: &mut Reader 'input, count: u32) -> ValueBuilder throws (binary::Error) {
162
    let buffer = try storage(input, @sizeOf(il::Val), @alignOf(il::Val), count);
163
    return ValueBuilder { storage: buffer, initialized: 0 };
164
}
165
166
/// Initialize the next operand. Return false when the storage is full.
167
fn pushValue(builder: &mut ValueBuilder, value: il::Val) -> bool {
168
    if builder.initialized == builder.storage.len {
169
        return false;
170
    }
171
    unsafe {
172
        let values = (&mut builder.storage[..]) as *unsafe mut [il::Val];
173
        set values[builder.initialized] = value;
174
    }
175
    set builder.initialized += 1;
176
    return true;
177
}
178
179
/// Consume a builder and publish its owned table only when it is complete.
180
/// Incomplete Copy operands need no element cleanup; the arena retains storage.
181
fn finishValues(builder: ValueBuilder) -> ValueCompletion {
182
    let case ValueBuilder { storage, initialized } = builder
183
        else panic "finishValues: invalid builder";
184
    if initialized <> storage.len {
185
        return ValueCompletion::Incomplete;
186
    }
187
    unsafe {
188
        return ValueCompletion::Complete(storage as *mut [il::Val]);
189
    }
190
}
191
192
/// Completion distinguishes an empty initialized table from incomplete storage.
193
union ValueCompletion {
194
    /// Fully initialized table, including zero operands.
195
    Complete(*mut [il::Val]),
196
    /// Storage discarded before all operands were initialized.
197
    Incomplete,
198
}
199
200
/// Fill an owned operand table with checked tagged values in input order.
201
fn fillValues 'input (input: &mut Reader 'input, builder: ValueBuilder) -> *mut [il::Val] throws (binary::Error) {
202
    let mut pending = builder;
203
    while pending.initialized < pending.storage.len {
204
        assert pushValue(&mut pending, try val(input));
205
    }
206
    let case ValueCompletion::Complete(result) = finishValues(pending)
207
        else panic "fillValues: incomplete operand table";
208
    return result;
209
}
210
211
/// Read a checked bin operation tag.
212
fn binOp 'input (input: &mut Reader 'input) -> il::BinOp throws (binary::Error) {
213
    let tag = try integer(input, 1) as u8;
214
    match tag {
215
        case super::BIN_ADD => return il::BinOp::Add,
216
        case super::BIN_SUB => return il::BinOp::Sub,
217
        case super::BIN_MUL => return il::BinOp::Mul,
218
        case super::BIN_SDIV => return il::BinOp::Sdiv,
219
        case super::BIN_UDIV => return il::BinOp::Udiv,
220
        case super::BIN_SREM => return il::BinOp::Srem,
221
        case super::BIN_UREM => return il::BinOp::Urem,
222
        case super::BIN_EQ => return il::BinOp::Eq,
223
        case super::BIN_NE => return il::BinOp::Ne,
224
        case super::BIN_SLT => return il::BinOp::Slt,
225
        case super::BIN_SGE => return il::BinOp::Sge,
226
        case super::BIN_ULT => return il::BinOp::Ult,
227
        case super::BIN_UGE => return il::BinOp::Uge,
228
        case super::BIN_AND => return il::BinOp::And,
229
        case super::BIN_OR => return il::BinOp::Or,
230
        case super::BIN_XOR => return il::BinOp::Xor,
231
        case super::BIN_SHL => return il::BinOp::Shl,
232
        case super::BIN_SSHR => return il::BinOp::Sshr,
233
        case super::BIN_USHR => return il::BinOp::Ushr,
234
        else => throw binary::Error::Invalid,
235
    }
236
}
237
238
/// Read a checked un operation tag.
239
fn unOp 'input (input: &mut Reader 'input) -> il::UnOp throws (binary::Error) {
240
    let tag = try integer(input, 1) as u8;
241
    match tag {
242
        case super::UN_NEG => return il::UnOp::Neg,
243
        case super::UN_NOT => return il::UnOp::Not,
244
        else => throw binary::Error::Invalid,
245
    }
246
}
247
248
/// Read a checked cmp operation tag.
249
fn cmpOp 'input (input: &mut Reader 'input) -> il::CmpOp throws (binary::Error) {
250
    let tag = try integer(input, 1) as u8;
251
    match tag {
252
        case super::CMP_EQ => return il::CmpOp::Eq,
253
        case super::CMP_NE => return il::CmpOp::Ne,
254
        case super::CMP_SLT => return il::CmpOp::Slt,
255
        case super::CMP_ULT => return il::CmpOp::Ult,
256
        else => throw binary::Error::Invalid,
257
    }
258
}
259
260
/// Read an instruction with inline operands.
261
fn fixedInstr 'input (input: &mut Reader 'input, tag: u8) -> il::Instr throws (binary::Error) {
262
    match tag {
263
        case super::INSTR_RESERVE => {
264
            let vdst = try reg(input);
265
            let vsize = try val(input);
266
            let valignment = try integer(input, 4) as u32;
267
            return il::Instr::Reserve { dst: vdst, size: vsize, alignment: valignment };
268
        },
269
        case super::INSTR_LOAD, super::INSTR_SLOAD => {
270
            let vtyp = try typ(input);
271
            let vdst = try reg(input);
272
            let vsrc = try reg(input);
273
            let voffset = try integer(input, 4) as i32;
274
            if tag == super::INSTR_SLOAD {
275
                return il::Instr::Sload { typ: vtyp, dst: vdst, src: vsrc, offset: voffset };
276
            }
277
            return il::Instr::Load { typ: vtyp, dst: vdst, src: vsrc, offset: voffset };
278
        },
279
        case super::INSTR_STORE => {
280
            let vtyp = try typ(input);
281
            let vsrc = try val(input);
282
            let vdst = try reg(input);
283
            let voffset = try integer(input, 4) as i32;
284
            return il::Instr::Store { typ: vtyp, src: vsrc, dst: vdst, offset: voffset };
285
        },
286
        case super::INSTR_BLIT => {
287
            let vdst = try reg(input);
288
            let vsrc = try reg(input);
289
            let vsize = try val(input);
290
            return il::Instr::Blit { dst: vdst, src: vsrc, size: vsize };
291
        },
292
        case super::INSTR_COPY => {
293
            let vdst = try reg(input);
294
            let vval = try val(input);
295
            return il::Instr::Copy { dst: vdst, val: vval };
296
        },
297
        case super::INSTR_BINOP => {
298
            let vop = try binOp(input);
299
            let vtyp = try typ(input);
300
            let vdst = try reg(input);
301
            let va = try val(input);
302
            let vb = try val(input);
303
            return il::Instr::BinOp { op: vop, typ: vtyp, dst: vdst, a: va, b: vb };
304
        },
305
        case super::INSTR_UNOP => {
306
            let vop = try unOp(input);
307
            let vtyp = try typ(input);
308
            let vdst = try reg(input);
309
            let va = try val(input);
310
            return il::Instr::UnOp { op: vop, typ: vtyp, dst: vdst, a: va };
311
        },
312
        case super::INSTR_ZEXT, super::INSTR_SEXT => {
313
            let vtyp = try typ(input);
314
            let vdst = try reg(input);
315
            let vval = try val(input);
316
            if tag == super::INSTR_SEXT {
317
                return il::Instr::Sext { typ: vtyp, dst: vdst, val: vval };
318
            }
319
            return il::Instr::Zext { typ: vtyp, dst: vdst, val: vval };
320
        },
321
        case super::INSTR_RET => {
322
            let mut vval: ?il::Val = nil;
323
            if try flag(input) {
324
                set vval = try val(input);
325
            }
326
            return il::Instr::Ret { val: vval };
327
        },
328
        case super::INSTR_UNREACHABLE => {
329
            return il::Instr::Unreachable;
330
        },
331
        case super::INSTR_ECALL => {
332
            let vdst = try reg(input);
333
            let vnum = try val(input);
334
            let va0 = try val(input);
335
            let va1 = try val(input);
336
            let va2 = try val(input);
337
            let va3 = try val(input);
338
            return il::Instr::Ecall { dst: vdst, num: vnum, a0: va0, a1: va1, a2: va2, a3: va3 };
339
        },
340
        case super::INSTR_DEVICE_READ => {
341
            let t = try typ(input);
342
            let dst = try reg(input);
343
            let handle = try val(input); let offset = try val(input);
344
            return il::Instr::DeviceRead { typ: t, dst, handle, offset };
345
        },
346
        case super::INSTR_DEVICE_WRITE => {
347
            let t = try typ(input);
348
            let handle = try val(input); let offset = try val(input); let value = try val(input);
349
            return il::Instr::DeviceWrite { typ: t, handle, offset, value };
350
        },
351
        case super::INSTR_EBREAK => {
352
            return il::Instr::Ebreak;
353
        },
354
        case super::INSTR_MEMORYFENCE => {
355
            return il::Instr::MemoryFence;
356
        },
357
        else => throw binary::Error::Invalid,
358
    }
359
}
360
361
/// Read one instruction and reconstruct its operand sequences.
362
export unsafe fn instr 'input (input: &mut Reader 'input) -> il::Instr throws (binary::Error) {
363
    let tag = try integer(input, 1) as u8;
364
    match tag {
365
        case super::INSTR_CALL => {
366
            let vretTy = try typ(input);
367
            let mut vdst: ?il::Reg = nil;
368
            if try flag(input) {
369
                set vdst = try reg(input);
370
            }
371
            let vfunc = try val(input);
372
            let vargs = try values(input);
373
            return il::Instr::Call { retTy: vretTy, dst: vdst, func: vfunc, args: vargs };
374
        },
375
        case super::INSTR_JMP => {
376
            let vtarget = try target(input);
377
            let vargs = try values(input);
378
            return il::Instr::Jmp { target: vtarget, args: vargs };
379
        },
380
        case super::INSTR_BR => {
381
            let vop = try cmpOp(input);
382
            let vtyp = try typ(input);
383
            let va = try val(input);
384
            let vb = try val(input);
385
            let vthenTarget = try target(input);
386
            let vthenArgs = try values(input);
387
            let velseTarget = try target(input);
388
            let velseArgs = try values(input);
389
            return il::Instr::Br { op: vop, typ: vtyp, a: va, b: vb, thenTarget: vthenTarget, thenArgs: vthenArgs, elseTarget: velseTarget, elseArgs: velseArgs };
390
        },
391
        case super::INSTR_SWITCH => {
392
            let vval = try val(input);
393
            let vdefaultTarget = try target(input);
394
            let vdefaultArgs = try values(input);
395
            let n = try count(input, 16);
396
            let caseStorage = try storage(input, @sizeOf(il::SwitchCase), @alignOf(il::SwitchCase), n)
397
                as *mut [il::SwitchCase];
398
            let vcases = try fillCases(input, caseStorage);
399
            return il::Instr::Switch { val: vval, defaultTarget: vdefaultTarget, defaultArgs: vdefaultArgs, cases: (&mut vcases[..]) as *unsafe mut [il::SwitchCase] };
400
        },
401
        else => return try fixedInstr(input, tag),
402
    }
403
}
404
405
/// Fill owned switch cases with signed values and checked block targets.
406
fn fillCases 'input (input: &mut Reader 'input, cases: *mut [il::SwitchCase]) -> *mut [il::SwitchCase] throws (binary::Error) {
407
    for i in 0..cases.len {
408
        let value = try integer(input, 8) as i64;
409
        let block = try target(input);
410
        unsafe {
411
            let args = try values(input);
412
            set cases[i] = il::SwitchCase { value, target: block, args };
413
        }
414
    }
415
    return cases;
416
}
417
418
/// Read an initializer with a repetition count.
419
export unsafe fn dataValue 'input (input: &mut Reader 'input) -> il::DataValue throws (binary::Error) {
420
    let tag = try integer(input, 1) as u8;
421
    let item = il::DataItem::Str(try bytes(input)) if tag == super::DATA_STR
422
        else try fixedDataItem(input, tag);
423
    let n = try integer(input, 4) as u32;
424
    return il::DataValue { item, count: n };
425
}
426
427
/// Decode a data item whose payload needs no allocation.
428
fn fixedDataItem 'input (input: &mut Reader 'input, tag: u8) -> il::DataItem throws (binary::Error) {
429
    match tag {
430
        case super::DATA_VAL => {
431
            let t = try typ(input);
432
            let n = try integer(input, il::typeSize(t));
433
            return il::DataItem::Val { typ: t, val: n as i64 };
434
        },
435
        case super::DATA_SYM => {
436
            return il::DataItem::Sym(try symbol(input));
437
        },
438
        case super::DATA_FN => {
439
            return il::DataItem::Fn(try symbol(input));
440
        },
441
        case super::DATA_UNDEF => {
442
            return il::DataItem::Undef;
443
        },
444
        else => throw binary::Error::Invalid,
445
    }
446
}