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