compiler: Borrow binary writer storage by lexical region

44316af275bbf7a97136a5f38cdb43407d186d5184daa119fe015c66b7b97a45
Alexis Sellier committed ago 1 parent d98e577a
lib/std/lang/il/binary/program.rad +57 -55
8 8
use std::lang::il::binary::writer;
9 9
use std::lang::alloc;
10 10
use std::mem;
11 11
12 12
/// Write a counted sequence of typed SSA parameters.
13 -
unsafe fn writeParams(out: &mut writer::Writer, params: &[il::Param]) throws (binary::Error) {
13 +
fn writeParams 'buffer (out: &mut writer::Writer 'buffer, params: &[il::Param]) throws (binary::Error) {
14 14
    try writer::integer(out, params.len as u64, 4);
15 15
    for param in params {
16 16
        try writer::integer(out, param.value.n as u64, 4);
17 17
        try writer::typ(out, param.type);
18 18
    }
19 19
}
20 20
21 21
/// Write a package. Return the number of encoded bytes.
22 22
/// All package tables and IL storage must remain valid during encoding.
23 23
export unsafe fn encode(bytes: &mut [u8], package: &binary::Package) -> u32 throws (binary::Error) {
24 -
    let mut out = writer::new(bytes, package.symbols);
25 -
    try writer::integer(&mut out, binary::MAGIC as u64, 4);
26 -
    try writer::integer(&mut out, binary::VERSION as u64, 4);
27 -
    try writer::integer(&mut out, package.symbols.len as u64, 4);
28 -
    for name in package.symbols {
29 -
        try writer::bytes(&mut out, name);
30 -
    }
31 -
    try writer::symbol(&mut out, package.name);
32 -
    try writer::integer(&mut out, package.dependencies.len as u64, 4);
33 -
    for name in package.dependencies {
34 -
        try writer::symbol(&mut out, name);
35 -
    }
36 -
    try writer::integer(&mut out, package.exports.len as u64, 4);
37 -
    for item in package.exports {
38 -
        try writer::symbol(&mut out, item.name);
39 -
        match item.kind {
40 -
            case binary::ExportKind::Function => try writer::integer(&mut out, 0, 1),
41 -
            case binary::ExportKind::Data => try writer::integer(&mut out, 1, 1),
24 +
    let output: 'output = &mut bytes[..], symbols = &package.symbols[..] in {
25 +
        let mut out = writer::new(output, symbols);
26 +
        try writer::integer(&mut out, binary::MAGIC as u64, 4);
27 +
        try writer::integer(&mut out, binary::VERSION as u64, 4);
28 +
        try writer::integer(&mut out, package.symbols.len as u64, 4);
29 +
        for name in package.symbols {
30 +
            try writer::bytes(&mut out, name);
42 31
        }
43 -
    }
44 -
    if let entry = package.entry {
45 -
        try writer::integer(&mut out, 1, 1);
46 -
        try writer::symbol(&mut out, entry);
47 -
    } else {
48 -
        try writer::integer(&mut out, 0, 1);
49 -
    }
50 -
    try writer::integer(&mut out, package.program.data.len as u64, 4);
51 -
    for item in package.program.data {
52 -
        try writer::symbol(&mut out, item.name);
53 -
        try writer::integer(&mut out, item.size as u64, 4);
54 -
        try writer::integer(&mut out, item.alignment as u64, 4);
55 -
        try writer::integer(&mut out, 1 if item.readOnly else 0, 1);
56 -
        try writer::integer(&mut out, 1 if item.isZeroInit else 0, 1);
57 -
        try writer::integer(&mut out, item.values.len as u64, 4);
58 -
        for value in item.values {
59 -
            try writer::dataValue(&mut out, value);
32 +
        try writer::symbol(&mut out, package.name);
33 +
        try writer::integer(&mut out, package.dependencies.len as u64, 4);
34 +
        for name in package.dependencies {
35 +
            try writer::symbol(&mut out, name);
60 36
        }
61 -
    }
62 -
    try writer::integer(&mut out, package.program.fns.len as u64, 4);
63 -
    for func in package.program.fns {
64 -
        try writer::symbol(&mut out, func.name);
65 -
        try writer::typ(&mut out, func.returnType);
66 -
        try writer::integer(&mut out, 1 if func.isExtern else 0, 1);
67 -
        try writeParams(&mut out, func.params);
68 -
        try writer::integer(&mut out, func.blocks.len as u64, 4);
69 -
        for block in func.blocks {
70 -
            try writer::bytes(&mut out, block.label);
71 -
            try writeParams(&mut out, block.params);
72 -
            try writer::integer(&mut out, block.loopDepth as u64, 4);
73 -
            try writer::integer(&mut out, block.preds.len as u64, 4);
74 -
            for pred in block.preds {
75 -
                try writer::integer(&mut out, pred as u64, 4);
37 +
        try writer::integer(&mut out, package.exports.len as u64, 4);
38 +
        for item in package.exports {
39 +
            try writer::symbol(&mut out, item.name);
40 +
            match item.kind {
41 +
                case binary::ExportKind::Function => try writer::integer(&mut out, 0, 1),
42 +
                case binary::ExportKind::Data => try writer::integer(&mut out, 1, 1),
76 43
            }
77 -
            try writer::integer(&mut out, block.instrs.len as u64, 4);
78 -
            for instr in block.instrs {
79 -
                try writer::instr(&mut out, instr);
44 +
        }
45 +
        if let entry = package.entry {
46 +
            try writer::integer(&mut out, 1, 1);
47 +
            try writer::symbol(&mut out, entry);
48 +
        } else {
49 +
            try writer::integer(&mut out, 0, 1);
50 +
        }
51 +
        try writer::integer(&mut out, package.program.data.len as u64, 4);
52 +
        for item in package.program.data {
53 +
            try writer::symbol(&mut out, item.name);
54 +
            try writer::integer(&mut out, item.size as u64, 4);
55 +
            try writer::integer(&mut out, item.alignment as u64, 4);
56 +
            try writer::integer(&mut out, 1 if item.readOnly else 0, 1);
57 +
            try writer::integer(&mut out, 1 if item.isZeroInit else 0, 1);
58 +
            try writer::integer(&mut out, item.values.len as u64, 4);
59 +
            for value in item.values {
60 +
                try writer::dataValue(&mut out, value);
61 +
            }
62 +
        }
63 +
        try writer::integer(&mut out, package.program.fns.len as u64, 4);
64 +
        for func in package.program.fns {
65 +
            try writer::symbol(&mut out, func.name);
66 +
            try writer::typ(&mut out, func.returnType);
67 +
            try writer::integer(&mut out, 1 if func.isExtern else 0, 1);
68 +
            try writeParams(&mut out, func.params);
69 +
            try writer::integer(&mut out, func.blocks.len as u64, 4);
70 +
            for block in func.blocks {
71 +
                try writer::bytes(&mut out, block.label);
72 +
                try writeParams(&mut out, block.params);
73 +
                try writer::integer(&mut out, block.loopDepth as u64, 4);
74 +
                try writer::integer(&mut out, block.preds.len as u64, 4);
75 +
                for pred in block.preds {
76 +
                    try writer::integer(&mut out, pred as u64, 4);
77 +
                }
78 +
                try writer::integer(&mut out, block.instrs.len as u64, 4);
79 +
                for instr in block.instrs {
80 +
                    try writer::instr(&mut out, instr);
81 +
                }
80 82
            }
81 83
        }
84 +
        return out.offset;
82 85
    }
83 -
    return out.offset;
84 86
}
85 87
86 88
/// Read typed SSA parameters with checked register indices.
87 89
unsafe fn readParams(input: &mut reader::Reader) -> *unsafe [il::Param] throws (binary::Error) {
88 90
    let n = try reader::count(input, 5);
lib/std/lang/il/binary/tests.rad +187 -161
9 9
10 10
/// Decode arena backing storage. Tests reset it before each use.
11 11
static MEMORY: [u8; 512] = [0; 512];
12 12
13 13
/// Check little-endian encoding for every integer width.
14 -
@test unsafe fn integers() throws (testing::TestError) {
14 +
@test fn integers() throws (testing::TestError) {
15 15
    let mut buffer: [u8; 15] = [0; 15];
16 -
    let mut out = writer::new(&mut buffer[..], &[]);
17 -
    try writer::integer(&mut out, 0x12, 1) catch {
18 -
        throw testing::TestError::Failed;
19 -
    };
20 -
    try writer::integer(&mut out, 0x3456, 2) catch {
21 -
        throw testing::TestError::Failed;
22 -
    };
23 -
    try writer::integer(&mut out, 0x789abcde, 4) catch {
24 -
        throw testing::TestError::Failed;
25 -
    };
26 -
    try writer::integer(&mut out, 0x0123456789abcdef, 8) catch {
27 -
        throw testing::TestError::Failed;
28 -
    };
29 -
    try testing::expectBytesEq(&buffer[..], &[0x12, 0x56, 0x34, 0xde, 0xbc, 0x9a, 0x78, 0xef, 0xcd,
30 -
        0xab, 0x89, 0x67, 0x45, 0x23, 0x01]);
16 +
    let namesTable: [*[u8]; 0] = [];
17 +
    let storage: 'buffer = &mut buffer[..], names = &namesTable[..] in {
18 +
        let mut out = writer::new(storage, names);
19 +
        try writer::integer(&mut out, 0x12, 1) catch {
20 +
            throw testing::TestError::Failed;
21 +
        };
22 +
        try writer::integer(&mut out, 0x3456, 2) catch {
23 +
            throw testing::TestError::Failed;
24 +
        };
25 +
        try writer::integer(&mut out, 0x789abcde, 4) catch {
26 +
            throw testing::TestError::Failed;
27 +
        };
28 +
        try writer::integer(&mut out, 0x0123456789abcdef, 8) catch {
29 +
            throw testing::TestError::Failed;
30 +
        };
31 +
        try testing::expectBytesEq(&out.bytes[..], &[0x12, 0x56, 0x34, 0xde, 0xbc, 0x9a, 0x78, 0xef, 0xcd,
32 +
            0xab, 0x89, 0x67, 0x45, 0x23, 0x01]);
33 +
    }
31 34
}
32 35
33 36
/// Compare one instruction with its fixed wire representation.
34 37
unsafe fn instruction(item: il::Instr, expected: &[u8]) throws (testing::TestError) {
35 38
    let mut buffer: [u8; 256] = [0; 256];
36 -
    let mut out = writer::new(&mut buffer[..], &["data", "fn"]);
37 -
    try writer::instr(&mut out, item) catch {
38 -
        throw testing::TestError::Failed;
39 -
    };
40 -
    try testing::expectBytesEq(&buffer[..out.offset], expected);
41 -
    for capacity in 0..expected.len {
42 -
        let mut short = writer::new(&mut buffer[..capacity], &["data", "fn"]);
43 -
        let mut failed = false;
44 -
        try writer::instr(&mut short, item) catch err {
45 -
            try testing::expect(err == binary::Error::Capacity);
46 -
            set failed = true;
39 +
    let namesTable: [*[u8]; 2] = ["data", "fn"];
40 +
    let storage: 'buffer = &mut buffer[..], names = &namesTable[..] in {
41 +
        let mut out = writer::new(storage, names);
42 +
        try writer::instr(&mut out, item) catch {
43 +
            throw testing::TestError::Failed;
47 44
        };
48 -
        try testing::expect(failed);
49 -
        try testing::expect(short.offset <= capacity);
50 -
    }
51 -
    let memory = &mut MEMORY[..512];
52 -
    let mut arena = alloc::new(&mut memory[..]);
53 -
    let mut input = reader::new(expected, &mut arena, &["data", "fn"]);
54 -
    set input.registers = 16;
55 -
    set input.blocks = 4;
56 -
    let decoded = try reader::instr(&mut input) catch {
57 -
        throw testing::TestError::Failed;
58 -
    };
59 -
    try testing::expect(input.offset == expected.len);
60 -
    set out.offset = 0;
61 -
    try writer::instr(&mut out, decoded) catch {
62 -
        throw testing::TestError::Failed;
63 -
    };
64 -
    try testing::expectBytesEq(&buffer[..out.offset], expected);
65 -
    for length in 0..expected.len {
66 -
        alloc::reset(&mut arena);
67 -
        set input = reader::new(&expected[..length], &mut arena, &["data", "fn"]);
45 +
        try testing::expectBytesEq(&out.bytes[..out.offset], expected);
46 +
        for capacity in 0..expected.len {
47 +
            let shortStorage: 'short = &mut out.bytes[..capacity], shortNames = &out.symbols[..] in {
48 +
                let mut short = writer::new(shortStorage, shortNames);
49 +
                let mut failed = false;
50 +
                try writer::instr(&mut short, item) catch err {
51 +
                    try testing::expect(err == binary::Error::Capacity);
52 +
                    set failed = true;
53 +
                };
54 +
                try testing::expect(failed);
55 +
                try testing::expect(short.offset <= capacity);
56 +
            }
57 +
        }
58 +
        let memory = &mut MEMORY[..512];
59 +
        let mut arena = alloc::new(&mut memory[..]);
60 +
        let mut input = reader::new(expected, &mut arena, &["data", "fn"]);
68 61
        set input.registers = 16;
69 62
        set input.blocks = 4;
70 -
        let mut failed = false;
71 -
        try reader::instr(&mut input) catch err {
72 -
            try testing::expect(err == binary::Error::Truncated);
73 -
            set failed = true;
63 +
        let decoded = try reader::instr(&mut input) catch {
64 +
            throw testing::TestError::Failed;
74 65
        };
75 -
        try testing::expect(failed);
76 -
    }
66 +
        try testing::expect(input.offset == expected.len);
67 +
        set out.offset = 0;
68 +
        try writer::instr(&mut out, decoded) catch {
69 +
            throw testing::TestError::Failed;
70 +
        };
71 +
        try testing::expectBytesEq(&out.bytes[..out.offset], expected);
72 +
        for length in 0..expected.len {
73 +
            alloc::reset(&mut arena);
74 +
            set input = reader::new(&expected[..length], &mut arena, &["data", "fn"]);
75 +
            set input.registers = 16;
76 +
            set input.blocks = 4;
77 +
            let mut failed = false;
78 +
            try reader::instr(&mut input) catch err {
79 +
                try testing::expect(err == binary::Error::Truncated);
80 +
                set failed = true;
81 +
            };
82 +
            try testing::expect(failed);
83 +
        }
77 84
85 +
    }
78 86
}
79 87
80 88
/// Check all instruction tags and their field order.
81 89
@test unsafe fn instructions() throws (testing::TestError) {
82 90
    let mut args: [il::Val; 1] = [il::Val::Undef];
162 170
}
163 171
164 172
/// Check every value tag and empty and nonempty sequences.
165 173
@test unsafe fn values() throws (testing::TestError) {
166 174
    let mut buffer: [u8; 64] = [0; 64];
167 -
    let mut out = writer::new(&mut buffer[..], &["data", "fn"]);
168 -
    try writer::values(&mut out, &[]) catch {
169 -
        throw testing::TestError::Failed;
170 -
    };
171 -
    try writer::values(&mut out, &[
172 -
        il::Val::Reg(il::Reg { n: 0x12345678 }),
173 -
        il::Val::Imm(-2), il::Val::DataSym("data"),
174 -
        il::Val::FnAddr("fn"), il::Val::Undef,
175 -
    ]) catch {
176 -
        throw testing::TestError::Failed;
177 -
    };
178 -
    try testing::expectBytesEq(&buffer[..out.offset], &[
179 -
        0, 0, 0, 0, 5, 0, 0, 0,
180 -
        0, 0x78, 0x56, 0x34, 0x12,
181 -
        1, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
182 -
        2, 0, 0, 0, 0, 3, 1, 0, 0, 0, 4,
183 -
    ]);
184 -
    let memory = &mut MEMORY[..256];
185 -
    let mut arena = alloc::new(&mut memory[..]);
186 -
    let mut input = reader::new(&buffer[..out.offset], &mut arena, &["data", "fn"]);
187 -
    set input.registers = 0x12345679;
188 -
    let empty = try reader::values(&mut input) catch {
189 -
        throw testing::TestError::Failed;
190 -
    };
191 -
    let decoded = try reader::values(&mut input) catch {
192 -
        throw testing::TestError::Failed;
193 -
    };
194 -
    try testing::expect(empty.len == 0);
195 -
    try testing::expect(decoded.len == 5);
196 -
    try testing::expect(input.offset == out.offset);
197 -
    let mut repeated: [u8; 64] = [0; 64];
198 -
    let mut copy = writer::new(&mut repeated[..], &["data", "fn"]);
199 -
    try writer::values(&mut copy, empty) catch {
200 -
        throw testing::TestError::Failed;
201 -
    };
202 -
    try writer::values(&mut copy, decoded) catch {
203 -
        throw testing::TestError::Failed;
204 -
    };
205 -
    try testing::expectBytesEq(&buffer[..out.offset], &repeated[..copy.offset]);
206 -
207 -
}
208 -
209 -
/// Check initializer bytes and repetition counts.
210 -
@test unsafe fn initializers() throws (testing::TestError) {
211 -
    let mut buffer: [u8; 128] = [0; 128];
212 -
    let mut out = writer::new(&mut buffer[..], &["data", "fn"]);
213 -
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W8,
214 -
        val: -1 }, count: 0 }) catch {
175 +
    let namesTable: [*[u8]; 2] = ["data", "fn"];
176 +
    let storage: 'buffer = &mut buffer[..], names = &namesTable[..] in {
177 +
        let mut out = writer::new(storage, names);
178 +
        try writer::values(&mut out, &[]) catch {
215 179
            throw testing::TestError::Failed;
216 180
        };
217 -
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W16,
218 -
        val: -2 }, count: 1 }) catch {
181 +
        try writer::values(&mut out, &[
182 +
            il::Val::Reg(il::Reg { n: 0x12345678 }),
183 +
            il::Val::Imm(-2), il::Val::DataSym("data"),
184 +
            il::Val::FnAddr("fn"), il::Val::Undef,
185 +
        ]) catch {
219 186
            throw testing::TestError::Failed;
220 187
        };
221 -
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W32,
222 -
        val: -3 }, count: 2 }) catch {
188 +
        try testing::expectBytesEq(&out.bytes[..out.offset], &[
189 +
            0, 0, 0, 0, 5, 0, 0, 0,
190 +
            0, 0x78, 0x56, 0x34, 0x12,
191 +
            1, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
192 +
            2, 0, 0, 0, 0, 3, 1, 0, 0, 0, 4,
193 +
        ]);
194 +
        let memory = &mut MEMORY[..256];
195 +
        let mut arena = alloc::new(&mut memory[..]);
196 +
        let mut input = reader::new(&out.bytes[..out.offset], &mut arena, &["data", "fn"]);
197 +
        set input.registers = 0x12345679;
198 +
        let empty = try reader::values(&mut input) catch {
223 199
            throw testing::TestError::Failed;
224 200
        };
225 -
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W64,
226 -
        val: -4 }, count: 3 }) catch {
201 +
        let decoded = try reader::values(&mut input) catch {
227 202
            throw testing::TestError::Failed;
228 203
        };
229 -
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Sym("data"), count: 4 })
230 -
        catch {
231 -
            throw testing::TestError::Failed;
232 -
        };
233 -
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Fn("fn"), count: 5 }) catch
234 -
        {
235 -
            throw testing::TestError::Failed;
236 -
        };
237 -
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Str("ab"), count: 6 }) catch
238 -
        {
239 -
            throw testing::TestError::Failed;
240 -
        };
241 -
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Str(""), count: 7 }) catch {
242 -
        throw testing::TestError::Failed; };
243 -
    try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Undef, count: 8 }) catch {
244 -
        throw testing::TestError::Failed; };
245 -
    try testing::expectBytesEq(&buffer[..out.offset], &[0, 1, 255, 0, 0, 0, 0, 0, 2, 254, 255, 1, 0,
246 -
        0, 0, 0, 4, 253, 255, 255, 255, 2, 0, 0, 0, 0, 8, 252, 255, 255, 255, 255, 255, 255, 255, 3,
247 -
        0, 0, 0, 1, 0, 0, 0, 0, 4, 0, 0, 0, 2, 1, 0, 0, 0, 5, 0, 0, 0, 3, 2, 0, 0, 0, 97, 98, 6, 0,
248 -
        0, 0, 3, 0, 0, 0, 0, 7, 0, 0, 0, 4, 8, 0, 0, 0]);
204 +
        try testing::expect(empty.len == 0);
205 +
        try testing::expect(decoded.len == 5);
206 +
        try testing::expect(input.offset == out.offset);
207 +
        let mut repeated: [u8; 64] = [0; 64];
208 +
        let copyNamesTable: [*[u8]; 2] = ["data", "fn"];
209 +
        let copyStorage: 'copy = &mut repeated[..], copyNames = &copyNamesTable[..] in {
210 +
            let mut copy = writer::new(copyStorage, copyNames);
211 +
            try writer::values(&mut copy, empty) catch {
212 +
                throw testing::TestError::Failed;
213 +
            };
214 +
            try writer::values(&mut copy, decoded) catch {
215 +
                throw testing::TestError::Failed;
216 +
            };
217 +
            try testing::expectBytesEq(&out.bytes[..out.offset], &copy.bytes[..copy.offset]);
218 +
219 +
        }
220 +
    }
221 +
}
222 +
223 +
/// Check initializer bytes and repetition counts.
224 +
@test fn initializers() throws (testing::TestError) {
225 +
    let mut buffer: [u8; 128] = [0; 128];
226 +
    let namesTable: [*[u8]; 2] = ["data", "fn"];
227 +
    let storage: 'buffer = &mut buffer[..], names = &namesTable[..] in {
228 +
        let mut out = writer::new(storage, names);
229 +
        try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W8,
230 +
            val: -1 }, count: 0 }) catch {
231 +
                throw testing::TestError::Failed;
232 +
            };
233 +
        try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W16,
234 +
            val: -2 }, count: 1 }) catch {
235 +
                throw testing::TestError::Failed;
236 +
            };
237 +
        try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W32,
238 +
            val: -3 }, count: 2 }) catch {
239 +
                throw testing::TestError::Failed;
240 +
            };
241 +
        try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Val { typ: il::Type::W64,
242 +
            val: -4 }, count: 3 }) catch {
243 +
                throw testing::TestError::Failed;
244 +
            };
245 +
        try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Sym("data"), count: 4 })
246 +
            catch {
247 +
                throw testing::TestError::Failed;
248 +
            };
249 +
        try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Fn("fn"), count: 5 }) catch
250 +
            {
251 +
                throw testing::TestError::Failed;
252 +
            };
253 +
        try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Str("ab"), count: 6 }) catch
254 +
            {
255 +
                throw testing::TestError::Failed;
256 +
            };
257 +
        try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Str(""), count: 7 }) catch {
258 +
            throw testing::TestError::Failed; };
259 +
        try writer::dataValue(&mut out, il::DataValue { item: il::DataItem::Undef, count: 8 }) catch {
260 +
            throw testing::TestError::Failed; };
261 +
        try testing::expectBytesEq(&out.bytes[..out.offset], &[0, 1, 255, 0, 0, 0, 0, 0, 2, 254, 255, 1, 0,
262 +
            0, 0, 0, 4, 253, 255, 255, 255, 2, 0, 0, 0, 0, 8, 252, 255, 255, 255, 255, 255, 255, 255, 3,
263 +
            0, 0, 0, 1, 0, 0, 0, 0, 4, 0, 0, 0, 2, 1, 0, 0, 0, 5, 0, 0, 0, 3, 2, 0, 0, 0, 97, 98, 6, 0,
264 +
            0, 0, 3, 0, 0, 0, 0, 7, 0, 0, 0, 4, 8, 0, 0, 0]);
265 +
    }
249 266
}
250 267
251 268
/// Reject invalid widths, insufficient storage, and absent symbols.
252 -
@test unsafe fn errors() throws (testing::TestError) {
269 +
@test fn errors() throws (testing::TestError) {
253 270
    let mut buffer: [u8; 8] = [0; 8];
254 -
    let mut out = writer::new(&mut buffer[..], &[]);
255 -
    let mut failures: u32 = 0;
256 -
    try writer::integer(&mut out, 1, 3) catch err {
257 -
        try testing::expect(err == binary::Error::Invalid);
258 -
        set failures += 1;
259 -
    };
260 -
    try writer::symbol(&mut out, "absent") catch err {
261 -
        try testing::expect(err == binary::Error::Symbol);
262 -
        set failures += 1;
263 -
    };
264 -
    try testing::expect(out.offset == 0);
265 -
    try writer::integer(&mut out, 0, 8) catch {
266 -
        throw testing::TestError::Failed;
267 -
    };
268 -
    try writer::integer(&mut out, 1, 1) catch err {
269 -
        try testing::expect(err == binary::Error::Capacity);
270 -
        set failures += 1;
271 -
    };
272 -
    try testing::expect(failures == 3);
273 -
    try testing::expect(out.offset == 8);
271 +
    let namesTable: [*[u8]; 0] = [];
272 +
    let storage: 'buffer = &mut buffer[..], names = &namesTable[..] in {
273 +
        let mut out = writer::new(storage, names);
274 +
        let mut failures: u32 = 0;
275 +
        try writer::integer(&mut out, 1, 3) catch err {
276 +
            try testing::expect(err == binary::Error::Invalid);
277 +
            set failures += 1;
278 +
        };
279 +
        try writer::symbol(&mut out, "absent") catch err {
280 +
            try testing::expect(err == binary::Error::Symbol);
281 +
            set failures += 1;
282 +
        };
283 +
        try testing::expect(out.offset == 0);
284 +
        try writer::integer(&mut out, 0, 8) catch {
285 +
            throw testing::TestError::Failed;
286 +
        };
287 +
        try writer::integer(&mut out, 1, 1) catch err {
288 +
            try testing::expect(err == binary::Error::Capacity);
289 +
            set failures += 1;
290 +
        };
291 +
        try testing::expect(failures == 3);
292 +
        try testing::expect(out.offset == 8);
293 +
    }
274 294
}
275 295
276 296
/// Check operation tags independently of the native union representation.
277 297
@test unsafe fn operations() throws (testing::TestError) {
278 298
    let binaryOps = &[
305 325
        }, &[13, tag as u8, 1, 4, 4, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0]);
306 326
    }
307 327
}
308 328
309 329
/// Check byte-sequence bounds at every output size.
310 -
@test unsafe fn byteCapacity() throws (testing::TestError) {
330 +
@test fn byteCapacity() throws (testing::TestError) {
311 331
    let mut buffer: [u8; 7] = [0; 7];
312 332
    for capacity in 0..7 {
313 -
        let mut out = writer::new(&mut buffer[..capacity], &[]);
314 -
        let mut failed = false;
315 -
        try writer::bytes(&mut out, "abc") catch err {
316 -
            try testing::expect(err == binary::Error::Capacity);
317 -
            set failed = true;
333 +
        let namesTable: [*[u8]; 0] = [];
334 +
        let storage: 'short = &mut buffer[..capacity], names = &namesTable[..] in {
335 +
            let mut out = writer::new(storage, names);
336 +
            let mut failed = false;
337 +
            try writer::bytes(&mut out, "abc") catch err {
338 +
                try testing::expect(err == binary::Error::Capacity);
339 +
                set failed = true;
340 +
            };
341 +
            try testing::expect(failed);
342 +
            try testing::expect(out.offset <= capacity);
343 +
        }
344 +
    }
345 +
    let namesTable: [*[u8]; 0] = [];
346 +
    let storage: 'buffer = &mut buffer[..], names = &namesTable[..] in {
347 +
        let mut out = writer::new(storage, names);
348 +
        try writer::bytes(&mut out, "abc") catch {
349 +
            throw testing::TestError::Failed;
318 350
        };
319 -
        try testing::expect(failed);
320 -
        try testing::expect(out.offset <= capacity);
351 +
        try testing::expectBytesEq(&out.bytes[..], &[3, 0, 0, 0, 97, 98, 99]);
321 352
    }
322 -
    let mut out = writer::new(&mut buffer[..], &[]);
323 -
    try writer::bytes(&mut out, "abc") catch {
324 -
        throw testing::TestError::Failed;
325 -
    };
326 -
    try testing::expectBytesEq(&buffer[..], &[3, 0, 0, 0, 97, 98, 99]);
327 353
}
lib/std/lang/il/binary/writer.rad +19 -18
4 4
use std::lang::il;
5 5
use std::mem;
6 6
use std::lang::il::binary;
7 7
8 8
/// Output cursor and the symbol table used by this package.
9 -
/// The output buffer and symbol table must remain valid until the last write.
10 -
export record Writer: Copy {
9 +
/// The buffer is exclusively borrowed for the writer's region.
10 +
export record Writer: 'buffer {
11 11
    /// Destination bytes.
12 -
    bytes: *unsafe mut [u8],
12 +
    bytes: &'buffer mut [u8],
13 13
    /// Number of bytes written.
14 14
    offset: u32,
15 15
    /// Names in wire-index order. The caller supplies unique names.
16 -
    symbols: *unsafe [*[u8]],
16 +
    symbols: &'buffer [*[u8]],
17 17
}
18 18
19 19
/// Create an output cursor without allocation.
20 -
/// The caller must retain exclusive access to the output buffer.
21 -
export unsafe fn new(bytes: &mut [u8], symbols: *unsafe [*[u8]]) -> Writer {
22 -
    return Writer { bytes: bytes as *unsafe mut [u8], offset: 0, symbols };
20 +
/// Retain the buffer and symbol table for the same region.
21 +
export fn new 'buffer (bytes: &'buffer mut [u8], symbols: &'buffer [*[u8]]) -> Writer 'buffer {
22 +
    return Writer 'buffer { bytes, offset: 0, symbols };
23 23
}
24 24
25 25
/// Write the low bytes of an integer with width 1, 2, 4, or 8.
26 -
export unsafe fn integer(out: &mut Writer, value: u64, width: u32) throws (binary::Error) {
26 +
export fn integer 'buffer (out: &mut Writer 'buffer, value: u64, width: u32) throws (binary::Error) {
27 27
    if width <> 1 and width <> 2 and width <> 4 and width <> 8 {
28 28
        throw binary::Error::Invalid;
29 29
    }
30 30
    if out.offset > out.bytes.len or width > out.bytes.len - out.offset {
31 31
        throw binary::Error::Capacity;
35 35
    }
36 36
    set out.offset += width;
37 37
}
38 38
39 39
/// Write a byte sequence with its u32 length.
40 -
export unsafe fn bytes(out: &mut Writer, value: &[u8]) throws (binary::Error) {
40 +
export fn bytes 'buffer (out: &mut Writer 'buffer, value: &[u8]) throws (binary::Error) {
41 41
    try integer(out, value.len as u64, 4);
42 42
    if value.len > out.bytes.len - out.offset {
43 43
        throw binary::Error::Capacity;
44 44
    }
45 45
    for b, i in value {
47 47
    }
48 48
    set out.offset += value.len;
49 49
}
50 50
51 51
/// Write the index of a name in the package symbol table.
52 -
export unsafe fn symbol(out: &mut Writer, name: &[u8]) throws (binary::Error) {
52 +
export fn symbol 'buffer (out: &mut Writer 'buffer, name: &[u8]) throws (binary::Error) {
53 53
    for candidate, i in out.symbols {
54 54
        if mem::eq(candidate, name) {
55 55
            try integer(out, i as u64, 4);
56 56
            return;
57 57
        }
58 58
    }
59 59
    throw binary::Error::Symbol;
60 60
}
61 61
62 62
/// Write an IL type as its byte width.
63 -
export unsafe fn typ(out: &mut Writer, value: il::Type) throws (binary::Error) {
63 +
export fn typ 'buffer (out: &mut Writer 'buffer, value: il::Type) throws (binary::Error) {
64 64
    try integer(out, il::typeSize(value) as u64, 1);
65 65
}
66 66
67 67
/// Write a tagged value. Symbol addresses use table indices.
68 -
export unsafe fn val(out: &mut Writer, value: il::Val) throws (binary::Error) {
68 +
export fn val 'buffer (out: &mut Writer 'buffer, value: il::Val) throws (binary::Error) {
69 69
    match value {
70 70
        case il::Val::Reg(reg) => {
71 71
            try integer(out, super::VALUE_REG as u64, 1);
72 72
            try integer(out, reg.n as u64, 4);
73 73
        },
86 86
        case il::Val::Undef => try integer(out, super::VALUE_UNDEF as u64, 1),
87 87
    }
88 88
}
89 89
90 90
/// Write a sequence of values.
91 -
export unsafe fn values(out: &mut Writer, items: &[il::Val]) throws (binary::Error) {
91 +
export fn values 'buffer (out: &mut Writer 'buffer, items: &[il::Val]) throws (binary::Error) {
92 92
    try integer(out, items.len as u64, 4);
93 93
    for item in items {
94 94
        try val(out, item);
95 95
    }
96 96
}
97 97
98 98
/// Write an explicit bin operation tag.
99 -
unsafe fn binOp(out: &mut Writer, value: il::BinOp) throws (binary::Error) {
99 +
fn binOp 'buffer (out: &mut Writer 'buffer, value: il::BinOp) throws (binary::Error) {
100 100
    match value {
101 101
        case il::BinOp::Add => try integer(out, super::BIN_ADD as u64, 1),
102 102
        case il::BinOp::Sub => try integer(out, super::BIN_SUB as u64, 1),
103 103
        case il::BinOp::Mul => try integer(out, super::BIN_MUL as u64, 1),
104 104
        case il::BinOp::Sdiv => try integer(out, super::BIN_SDIV as u64, 1),
119 119
        case il::BinOp::Ushr => try integer(out, super::BIN_USHR as u64, 1),
120 120
    }
121 121
}
122 122
123 123
/// Write an explicit un operation tag.
124 -
unsafe fn unOp(out: &mut Writer, value: il::UnOp) throws (binary::Error) {
124 +
fn unOp 'buffer (out: &mut Writer 'buffer, value: il::UnOp) throws (binary::Error) {
125 125
    match value {
126 126
        case il::UnOp::Neg => try integer(out, super::UN_NEG as u64, 1),
127 127
        case il::UnOp::Not => try integer(out, super::UN_NOT as u64, 1),
128 128
    }
129 129
}
130 130
131 131
/// Write an explicit cmp operation tag.
132 -
unsafe fn cmpOp(out: &mut Writer, value: il::CmpOp) throws (binary::Error) {
132 +
fn cmpOp 'buffer (out: &mut Writer 'buffer, value: il::CmpOp) throws (binary::Error) {
133 133
    match value {
134 134
        case il::CmpOp::Eq => try integer(out, super::CMP_EQ as u64, 1),
135 135
        case il::CmpOp::Ne => try integer(out, super::CMP_NE as u64, 1),
136 136
        case il::CmpOp::Slt => try integer(out, super::CMP_SLT as u64, 1),
137 137
        case il::CmpOp::Ult => try integer(out, super::CMP_ULT as u64, 1),
138 138
    }
139 139
}
140 140
141 141
/// Write one instruction. Fields follow the IL record declaration order.
142 -
export unsafe fn instr(out: &mut Writer, item: il::Instr) throws (binary::Error) {
142 +
/// All raw instruction operand tables must remain valid during encoding.
143 +
export unsafe fn instr 'buffer (out: &mut Writer 'buffer, item: il::Instr) throws (binary::Error) {
143 144
    match item {
144 145
        case il::Instr::Reserve { dst: vdst, size: vsize, alignment: valignment } => {
145 146
            try integer(out, super::INSTR_RESERVE as u64, 1);
146 147
            try integer(out, vdst.n as u64, 4);
147 148
            try val(out, vsize);
286 287
        },
287 288
    }
288 289
}
289 290
290 291
/// Write one data initializer and its repetition count.
291 -
export unsafe fn dataValue(out: &mut Writer, value: il::DataValue) throws (binary::Error) {
292 +
export fn dataValue 'buffer (out: &mut Writer 'buffer, value: il::DataValue) throws (binary::Error) {
292 293
    match value.item {
293 294
        case il::DataItem::Val { typ: t, val: n } => {
294 295
            try integer(out, super::DATA_VAL as u64, 1);
295 296
            try typ(out, t);
296 297
            try integer(out, n as u64, il::typeSize(t));