lib/std/lang/il/binary/program.rad 15.3 KiB raw
1
//! Binary package envelopes and IL program reconstruction.
2
//! Tables occur in this order: symbols, dependencies, exports, data, functions.
3
//! Block records carry labels, parameters, loop depth, predecessors, instructions.
4
5
use std::lang::il;
6
use std::lang::il::binary;
7
use std::lang::il::binary::reader;
8
use std::lang::il::binary::writer;
9
use std::lang::alloc;
10
use std::mem;
11
12
/// Write a counted sequence of typed SSA parameters.
13
fn writeParams 'buffer (out: &mut writer::Writer 'buffer, params: &[il::Param]) throws (binary::Error) {
14
    try writer::integer(out, params.len as u64, 4);
15
    for param in params {
16
        try writer::integer(out, param.value.n as u64, 4);
17
        try writer::typ(out, param.type);
18
    }
19
}
20
21
/// Write global data definitions and their initializer sequences.
22
fn writeData 'buffer (out: &mut writer::Writer 'buffer, items: &[il::Data]) throws (binary::Error) {
23
    try writer::integer(out, items.len as u64, 4);
24
    for item in items {
25
        try writer::symbol(out, item.name);
26
        try writer::integer(out, item.size as u64, 4);
27
        try writer::integer(out, item.alignment as u64, 4);
28
        try writer::integer(out, 1 if item.readOnly else 0, 1);
29
        try writer::integer(out, 1 if item.isZeroInit else 0, 1);
30
        try writer::integer(out, item.values.len as u64, 4);
31
        for value in item.values {
32
            try writer::dataValue(out, value);
33
        }
34
    }
35
}
36
37
/// Write package identity, symbol tables, exports, and the optional entry.
38
fn writeHeader 'buffer (
39
    out: &mut writer::Writer 'buffer,
40
    symbols: &[*[u8]],
41
    name: *[u8],
42
    dependencies: &[*[u8]],
43
    exports: &[binary::Export],
44
    entry: ?*[u8]
45
) throws (binary::Error) {
46
    try writer::integer(out, binary::MAGIC as u64, 4);
47
    try writer::integer(out, binary::VERSION as u64, 4);
48
    try writer::integer(out, symbols.len as u64, 4);
49
    for name in symbols {
50
        try writer::bytes(out, name);
51
    }
52
    try writer::symbol(out, name);
53
    try writer::integer(out, dependencies.len as u64, 4);
54
    for name in dependencies {
55
        try writer::symbol(out, name);
56
    }
57
    try writer::integer(out, exports.len as u64, 4);
58
    for item in exports {
59
        try writer::symbol(out, item.name);
60
        match item.kind {
61
            case binary::ExportKind::Function => try writer::integer(out, 0, 1),
62
            case binary::ExportKind::Data => try writer::integer(out, 1, 1),
63
        }
64
    }
65
    if let entry = entry {
66
        try writer::integer(out, 1, 1);
67
        try writer::symbol(out, entry);
68
    } else {
69
        try writer::integer(out, 0, 1);
70
    }
71
}
72
73
/// Write a package. Return the number of encoded bytes.
74
/// All package tables and IL storage must remain valid during encoding.
75
export unsafe fn encode(bytes: &mut [u8], package: &binary::Package) -> u32 throws (binary::Error) {
76
    let output: 'output = &mut bytes[..], symbols = &package.symbols[..] in {
77
        let mut out = writer::new(output, symbols);
78
        try writeHeader(&mut out, symbols, package.name, package.dependencies, package.exports, package.entry);
79
        try writeData(&mut out, &package.program.data[..]);
80
        try writeFunctions(&mut out, package.program.fns);
81
        return out.offset;
82
    }
83
}
84
85
/// Write a counted function-pointer table in wire order.
86
fn writeFunctions 'buffer (out: &mut writer::Writer 'buffer, functions: &[*unsafe il::Fn]) throws (binary::Error) {
87
    try writer::integer(out, functions.len as u64, 4);
88
    for i in 0..functions.len {
89
        unsafe {
90
            try writeFunction(out, functions[i]);
91
        }
92
    }
93
}
94
95
/// Write function metadata before its parameter and block tables.
96
fn writeFunction 'buffer (out: &mut writer::Writer 'buffer, func: &il::Fn) throws (binary::Error) {
97
    try writer::symbol(out, func.name);
98
    try writer::typ(out, func.returnType);
99
    try writer::integer(out, 1 if func.isExtern else 0, 1);
100
    unsafe {
101
        try writeParams(out, func.params);
102
        try writeBlocks(out, func.blocks);
103
    }
104
}
105
106
/// Write a counted block table in function order.
107
fn writeBlocks 'buffer (out: &mut writer::Writer 'buffer, blocks: &[il::Block]) throws (binary::Error) {
108
    try writer::integer(out, blocks.len as u64, 4);
109
    for i in 0..blocks.len {
110
        let block = &blocks[i];
111
        unsafe {
112
            try writeBlock(out, block.label, block.params, block.loopDepth, block.preds, block.instrs);
113
        }
114
    }
115
}
116
117
/// Write block metadata and counted predecessor and instruction tables.
118
fn writeBlock 'buffer (
119
    out: &mut writer::Writer 'buffer,
120
    label: *[u8],
121
    params: &[il::Param],
122
    loopDepth: u32,
123
    preds: &[u32],
124
    instructions: &[il::Instr],
125
) throws (binary::Error) {
126
    try writer::bytes(out, label);
127
    try writeParams(out, params);
128
    try writer::integer(out, loopDepth as u64, 4);
129
    try writer::integer(out, preds.len as u64, 4);
130
    for pred in preds {
131
        try writer::integer(out, pred as u64, 4);
132
    }
133
    try writer::integer(out, instructions.len as u64, 4);
134
    for instr in instructions {
135
        unsafe {
136
            try writer::instr(out, instr);
137
        }
138
    }
139
}
140
141
/// Read typed SSA parameters with checked register indices.
142
unsafe fn readParams 'input (input: &mut reader::Reader 'input) -> *unsafe [il::Param] throws (binary::Error) {
143
    let n = try reader::count(input, 5);
144
    let params = try reader::storage(input, @sizeOf(il::Param), @alignOf(il::Param), n)
145
        as *mut [il::Param];
146
    let initialized = try fillParams(input, params);
147
    return (&initialized[..]) as *unsafe [il::Param];
148
}
149
150
/// Fill owned parameter storage from checked register indices and type tags.
151
fn fillParams 'input (input: &mut reader::Reader 'input, params: *mut [il::Param]) -> *[il::Param] throws (binary::Error) {
152
    for i in 0..params.len {
153
        let value = try reader::reg(input);
154
        let t = try reader::typ(input);
155
        set params[i] = il::Param { value, type: t };
156
    }
157
    return params;
158
}
159
160
/// Extend an initializer extent without exceeding its declared storage.
161
fn extendDataExtent(extent: u64, value: il::DataValue, size: u32) -> u64 throws (binary::Error) {
162
    let mut width: u32 = 0;
163
    match value.item {
164
        case il::DataItem::Val { typ, .. } => {
165
            set width = il::typeSize(typ);
166
        },
167
        case il::DataItem::Sym(_), il::DataItem::Fn(_) => {
168
            set width = 8;
169
        },
170
        case il::DataItem::Str(text) => {
171
            set width = text.len;
172
        },
173
        case il::DataItem::Undef => {
174
            set width = 1;
175
        },
176
    }
177
    let amount = width as u64 * value.count as u64;
178
    if extent > size as u64 or amount > size as u64 - extent {
179
        throw binary::Error::Invalid;
180
    }
181
    return extent + amount;
182
}
183
184
/// Read global data and check initializer extents against declared storage.
185
unsafe fn readData 'input (input: &mut reader::Reader 'input) -> *[il::Data] throws (binary::Error) {
186
    let n = try reader::count(input, 18);
187
    let items = try reader::storage(input, @sizeOf(il::Data), @alignOf(il::Data), n)
188
        as *mut [il::Data];
189
    return try fillData(input, items);
190
}
191
192
/// Fill owned data definitions with checked metadata and initializer extents.
193
fn fillData 'input (input: &mut reader::Reader 'input, items: *mut [il::Data]) -> *[il::Data] throws (binary::Error) {
194
    for i in 0..items.len {
195
        let name = try reader::symbol(input);
196
        let size = try reader::integer(input, 4) as u32;
197
        let alignment = try reader::integer(input, 4) as u32;
198
        if alignment == 0 or (alignment & (alignment - 1)) <> 0 {
199
            throw binary::Error::Invalid;
200
        }
201
        let readOnly = try reader::flag(input);
202
        let isZeroInit = try reader::flag(input);
203
        let count = try reader::count(input, 5);
204
        unsafe {
205
            let values = try reader::storage(input, @sizeOf(il::DataValue), @alignOf(il::DataValue), count)
206
                as *mut [il::DataValue];
207
            let initialized = try fillDataValues(input, values, size);
208
            set items[i] = il::Data { name, size, alignment, readOnly, isZeroInit, values: initialized };
209
        }
210
    }
211
    return items;
212
}
213
214
/// Fill owned initializer storage within its declared byte extent.
215
fn fillDataValues 'input (input: &mut reader::Reader 'input, values: *mut [il::DataValue], size: u32)
216
    -> *[il::DataValue] throws (binary::Error)
217
{
218
    let mut extent: u64 = 0;
219
    for i in 0..values.len {
220
        unsafe {
221
            set values[i] = try reader::dataValue(input);
222
        }
223
        set extent = try extendDataExtent(extent, values[i], size);
224
    }
225
    return values;
226
}
227
228
/// Read functions with checked block and register indices.
229
unsafe fn readFunctions 'input (input: &mut reader::Reader 'input, limits: binary::Limits)
230
    -> *unsafe [*unsafe il::Fn] throws (binary::Error)
231
{
232
    let n = try reader::count(input, 14);
233
    let fns = try reader::storage(input, @sizeOf(*il::Fn), @alignOf(*il::Fn), n) as *mut [*unsafe il::Fn];
234
    set input.registers = limits.registers;
235
    for i in 0..n {
236
        let name = try reader::symbol(input);
237
        let returnType = try reader::typ(input);
238
        let isExtern = try reader::flag(input);
239
        let params = try readParams(input);
240
        let count = try reader::count(input, 20);
241
        if count > limits.blocks or (isExtern and count <> 0) {
242
            throw binary::Error::Invalid;
243
        }
244
        set input.blocks = count;
245
        let blocks = try reader::storage(input, @sizeOf(il::Block), @alignOf(il::Block), count)
246
            as *mut [il::Block];
247
        let mut isLeaf = true;
248
        for j in 0..count {
249
            let label = try reader::bytes(input);
250
            let blockParams = try readParams(input);
251
            let loopDepth = try reader::integer(input, 4) as u32;
252
            let predCount = try reader::count(input, 4);
253
            let predStorage = try reader::storage(input, @sizeOf(u32), @alignOf(u32), predCount) as *mut [u32];
254
            let preds = try fillPredecessors(input, predStorage);
255
            let instrCount = try reader::count(input, 1);
256
            let instrs = try reader::storage(input, @sizeOf(il::Instr), @alignOf(il::Instr), instrCount)
257
                as *mut [il::Instr];
258
            if try fillInstructions(input, &mut instrs[..]) {
259
                set isLeaf = false;
260
            }
261
            set blocks[j] = il::Block {
262
                label, params: blockParams, instrs: (&mut instrs[..]) as *unsafe mut [il::Instr], locs: &[], preds: (&preds[..]) as *unsafe [u32], loopDepth,
263
            };
264
        }
265
        let func = try reader::storage(input, @sizeOf(il::Fn), @alignOf(il::Fn), 1) as *mut [il::Fn];
266
        set func[0] = il::Fn { name, params, returnType, isExtern, isLeaf, blocks: (&blocks[..]) as *unsafe [il::Block] };
267
        set fns[i] = &func[0];
268
    }
269
    return (&fns[..]) as *unsafe [*unsafe il::Fn];
270
}
271
272
/// Fill owned predecessor storage with checked block indices.
273
fn fillPredecessors 'input (input: &mut reader::Reader 'input, preds: *mut [u32]) -> *[u32] throws (binary::Error) {
274
    for i in 0..preds.len {
275
        set preds[i] = try reader::target(input);
276
    }
277
    return preds;
278
}
279
280
/// Fill an instruction table and report whether it contains a call.
281
fn fillInstructions 'input (input: &mut reader::Reader 'input, instrs: &mut [il::Instr]) -> bool throws (binary::Error) {
282
    let mut hasCall = false;
283
    for i in 0..instrs.len {
284
        unsafe {
285
            set instrs[i] = try reader::instr(input);
286
        }
287
        if il::isCall(instrs[i]) {
288
            set hasCall = true;
289
        }
290
    }
291
    return hasCall;
292
}
293
294
/// Read the package tables. The input must contain exactly one package.
295
unsafe fn readPackage 'input (input: &mut reader::Reader 'input, limits: binary::Limits)
296
    -> binary::Package throws (binary::Error)
297
{
298
    let magic = try reader::integer(input, 4) as u32;
299
    let version = try reader::integer(input, 4) as u32;
300
    if magic <> binary::MAGIC or version <> binary::VERSION {
301
        throw binary::Error::Invalid;
302
    }
303
    let symbolCount = try reader::count(input, 4);
304
    let symbolStorage = try reader::storage(input, @sizeOf(*[u8]), @alignOf(*[u8]), symbolCount)
305
        as *mut [*[u8]];
306
    let symbols = try fillSymbols(input, symbolStorage);
307
    let source: 'tables = &input.bytes[..], names = &symbols[..] where 'input: 'tables in {
308
        let mut cursor = reader::new(source, input.arena, names);
309
        set cursor.offset = input.offset;
310
        let name = try reader::symbol(&mut cursor);
311
        let depCount = try reader::count(&mut cursor, 4);
312
        let dependencyStorage = try reader::storage(&mut cursor, @sizeOf(*[u8]), @alignOf(*[u8]), depCount)
313
            as *mut [*[u8]];
314
        let dependencies = try fillDependencies(&mut cursor, dependencyStorage);
315
        let exportCount = try reader::count(&mut cursor, 5);
316
        let exportStorage = try reader::storage(&mut cursor, @sizeOf(binary::Export), @alignOf(binary::Export), exportCount)
317
            as *mut [binary::Export];
318
        let exports = try fillExports(&mut cursor, exportStorage);
319
        let mut entry: ?*[u8] = nil;
320
        if try reader::flag(&mut cursor) {
321
            set entry = try reader::symbol(&mut cursor);
322
        }
323
        let data = try readData(&mut cursor);
324
        let fns = try readFunctions(&mut cursor, limits);
325
        if cursor.offset <> cursor.bytes.len {
326
            throw binary::Error::Invalid;
327
        }
328
        set input.offset = cursor.offset;
329
        return binary::Package { symbols: (&symbols[..]) as *unsafe [*[u8]], name, dependencies: (&dependencies[..]) as *unsafe [*[u8]], exports: (&exports[..]) as *unsafe [binary::Export], entry, program: il::Program { data, fns } };
330
    }
331
}
332
333
/// Fill owned symbol storage with distinct nonempty names.
334
fn fillSymbols 'input (input: &mut reader::Reader 'input, symbols: *mut [*[u8]]) -> *[*[u8]] throws (binary::Error) {
335
    for i in 0..symbols.len {
336
        unsafe {
337
            set symbols[i] = try reader::bytes(input);
338
        }
339
        let name = symbols[i];
340
        if name.len == 0 {
341
            throw binary::Error::Invalid;
342
        }
343
        for j in 0..i {
344
            if mem::eq(name, symbols[j]) {
345
                throw binary::Error::Invalid;
346
            }
347
        }
348
    }
349
    return symbols;
350
}
351
352
/// Fill owned dependency storage from checked symbol indices.
353
fn fillDependencies 'input (input: &mut reader::Reader 'input, dependencies: *mut [*[u8]]) -> *[*[u8]] throws (binary::Error) {
354
    for i in 0..dependencies.len {
355
        set dependencies[i] = try reader::symbol(input);
356
    }
357
    return dependencies;
358
}
359
360
/// Fill owned exports from checked symbol indices and kind flags.
361
fn fillExports 'input (input: &mut reader::Reader 'input, exports: *mut [binary::Export]) -> *[binary::Export] throws (binary::Error) {
362
    for i in 0..exports.len {
363
        let symbol = try reader::symbol(input);
364
        let kind = binary::ExportKind::Data if try reader::flag(input) else binary::ExportKind::Function;
365
        set exports[i] = binary::Export { name: symbol, kind };
366
    }
367
    return exports;
368
}
369
370
/// Decode one package. Restore the arena offset on every failure.
371
/// The caller must retain the decoded allocations until the last package use.
372
export unsafe fn decode(bytes: &[u8], arena: &mut alloc::Arena, limits: binary::Limits)
373
    -> binary::Package throws (binary::Error)
374
{
375
    let saved = alloc::save(arena);
376
    let namesTable: [*[u8]; 0] = [];
377
    let source: 'input = &bytes[..], names = &namesTable[..] in {
378
        let mut input = reader::new(source, &mut *arena, names);
379
        return try readPackage(&mut input, limits) catch err {
380
            alloc::restore(arena, saved);
381
            throw err;
382
        };
383
    }
384
}