compiler: Borrow package name collection tables by region

6d74348fcb847b8fc995536989e6188057bf1a01929d01c77bc02e9683ee71e6
Alexis Sellier committed ago 1 parent 44316af2
compiler/radiance.rad +20 -18
95 95
/// Maximum number of indexed names in one binary package.
96 96
constant MAX_PACKAGE_SYMBOLS: u32 = 16384;
97 97
/// Export workspace reused for each binary package.
98 98
unsafe static PACKAGE_EXPORTS: [binary::Export; MAX_PACKAGE_EXPORTS] = undefined;
99 99
/// Symbol workspace reused for each binary package.
100 -
unsafe static PACKAGE_SYMBOLS: [*[u8]; MAX_PACKAGE_SYMBOLS] = undefined;
100 +
static PACKAGE_SYMBOLS: [*[u8]; MAX_PACKAGE_SYMBOLS] = [""; MAX_PACKAGE_SYMBOLS];
101 101
102 102
/// Debug info file extension.
103 103
constant DEBUG_EXT: *[u8] = ".debug";
104 104
105 105
/// Maximum rodata size (4MB).
1271 1271
                functions.append(func, allocator);
1272 1272
            }
1273 1273
        }
1274 1274
        let local = il::Program { data: &dataItems[..], fns: functions };
1275 1275
        let selected = try packageExports(ctx, pkg, &local, &mut PACKAGE_EXPORTS[..], &mut *res.arena);
1276 -
        let mut dependencies: [*[u8]; MAX_PACKAGES] = undefined;
1277 -
        let mut names = collect::new(&mut PACKAGE_SYMBOLS[..], &mut dependencies[..]);
1278 -
        let image = try collect::package(&mut names, pkg.name, local, &PACKAGE_EXPORTS[..selected.count], selected.entry) catch {
1279 -
            throw error(&["cannot collect binary RIL package", pkg.name]);
1280 -
        };
1281 -
        let length = try program::encode(&mut FN_ARENA[..], &image) catch {
1282 -
            throw error(&["binary RIL output capacity exceeded", pkg.name]);
1283 -
        };
1284 -
        let mut path: [u8; MAX_PATH_LEN] = undefined;
1285 -
        let mut pos: u32 = 0;
1286 -
        for part in &[directory, "/", pkg.name, ".ril"] {
1287 -
            set pos += try mem::copy(&mut path[pos..MAX_PATH_LEN - 1], part) catch {
1288 -
                throw error(&["binary RIL output path is too long"]);
1276 +
        let mut dependencies: [*[u8]; MAX_PACKAGES] = [""; MAX_PACKAGES];
1277 +
        let symbolTable: 'names = &mut PACKAGE_SYMBOLS[..], dependencyTable = &mut dependencies[..] in {
1278 +
            let mut names = collect::new(symbolTable, dependencyTable);
1279 +
            let image = try collect::package(&mut names, pkg.name, local, &PACKAGE_EXPORTS[..selected.count], selected.entry) catch {
1280 +
                throw error(&["cannot collect binary RIL package", pkg.name]);
1289 1281
            };
1290 -
        }
1291 -
        set path[pos] = 0;
1292 -
        if not unix::writeFile(&path[..pos], &FN_ARENA[..length]) {
1293 -
            throw error(&["cannot write binary RIL package", pkg.name]);
1282 +
            let length = try program::encode(&mut FN_ARENA[..], &image) catch {
1283 +
                throw error(&["binary RIL output capacity exceeded", pkg.name]);
1284 +
            };
1285 +
            let mut path: [u8; MAX_PATH_LEN] = undefined;
1286 +
            let mut pos: u32 = 0;
1287 +
            for part in &[directory, "/", pkg.name, ".ril"] {
1288 +
                set pos += try mem::copy(&mut path[pos..MAX_PATH_LEN - 1], part) catch {
1289 +
                    throw error(&["binary RIL output path is too long"]);
1290 +
                };
1291 +
            }
1292 +
            set path[pos] = 0;
1293 +
            if not unix::writeFile(&path[..pos], &FN_ARENA[..length]) {
1294 +
                throw error(&["cannot write binary RIL package", pkg.name]);
1295 +
            }
1294 1296
        }
1295 1297
    }
1296 1298
}
1297 1299
1298 1300
/// Match a qualified definition to its package name.
lib/std/lang/il/binary/collect.rad +15 -15
3 3
use std::mem;
4 4
use std::lang::il;
5 5
use std::lang::il::binary;
6 6
7 7
/// Caller-owned storage for a package's indexed names.
8 -
/// Both tables must outlive the collector and all packages that use them.
9 -
export record Names: Copy {
8 +
/// Both tables are exclusively borrowed for the collector's region.
9 +
export record Names: 'tables {
10 10
    /// Unique names in first-use order.
11 -
    symbols: *unsafe mut [*[u8]],
11 +
    symbols: &'tables mut [*[u8]],
12 12
    /// Number of initialized symbol entries.
13 13
    symbolCount: u32,
14 14
    /// Unique dependency names in first-use order.
15 -
    dependencies: *unsafe mut [*[u8]],
15 +
    dependencies: &'tables mut [*[u8]],
16 16
    /// Number of initialized dependency entries.
17 17
    dependencyCount: u32,
18 18
}
19 19
20 20
/// Start collection with empty caller-provided tables.
21 -
/// The caller must retain exclusive access to both tables during collection.
22 -
export unsafe fn new(symbols: &mut [*[u8]], dependencies: &mut [*[u8]]) -> Names {
23 -
    return Names { symbols: symbols as *unsafe mut [*[u8]], symbolCount: 0, dependencies: dependencies as *unsafe mut [*[u8]], dependencyCount: 0 };
21 +
/// Retain both tables for the same region.
22 +
export fn new 'tables (symbols: &'tables mut [*[u8]], dependencies: &'tables mut [*[u8]]) -> Names 'tables {
23 +
    return Names 'tables { symbols, symbolCount: 0, dependencies, dependencyCount: 0 };
24 24
}
25 25
26 26
/// Add a unique name and check the symbol table's capacity.
27 -
unsafe fn add(names: &mut Names, name: *[u8]) throws (binary::Error) {
27 +
fn add 'tables (names: &mut Names 'tables, name: *[u8]) throws (binary::Error) {
28 28
    if name.len == 0 {
29 29
        throw binary::Error::Invalid;
30 30
    }
31 31
    for i in 0..names.symbolCount {
32 32
        if mem::eq(names.symbols[i], name) {
39 39
    set names.symbols[names.symbolCount] = name;
40 40
    set names.symbolCount += 1;
41 41
}
42 42
43 43
/// Add a qualified reference and its owning package dependency.
44 -
unsafe fn reference(names: &mut Names, owner: *[u8], name: *[u8]) throws (binary::Error) {
44 +
fn reference 'tables (names: &mut Names 'tables, owner: *[u8], name: *[u8]) throws (binary::Error) {
45 45
    try add(names, name);
46 46
    for i in 0..name.len {
47 47
        if name[i] == ':' and i + 1 < name.len and name[i + 1] == ':' {
48 48
            if i == 0 or i + 2 == name.len {
49 49
                throw binary::Error::Invalid;
68 68
    }
69 69
    throw binary::Error::Invalid;
70 70
}
71 71
72 72
/// Collect names referenced by one IL value.
73 -
unsafe fn value(names: &mut Names, owner: *[u8], item: il::Val) throws (binary::Error) {
73 +
fn value 'tables (names: &mut Names 'tables, owner: *[u8], item: il::Val) throws (binary::Error) {
74 74
    match item {
75 75
        case il::Val::DataSym(name) => try reference(names, owner, name),
76 76
        case il::Val::FnAddr(name) => try reference(names, owner, name),
77 77
        else => {
78 78
        },
79 79
    }
80 80
}
81 81
82 82
/// Collect names from an instruction's variable-length operand sequence.
83 -
unsafe fn values(names: &mut Names, owner: *[u8], items: &[il::Val]) throws (binary::Error) {
83 +
fn values 'tables (names: &mut Names 'tables, owner: *[u8], items: &[il::Val]) throws (binary::Error) {
84 84
    for item in items {
85 85
        try value(names, owner, item);
86 86
    }
87 87
}
88 88
89 89
/// Collect all symbolic instruction operands.
90 -
unsafe fn instruction(names: &mut Names, owner: *[u8], instr: il::Instr) throws (binary::Error) {
90 +
unsafe fn instruction 'tables (names: &mut Names 'tables, owner: *[u8], instr: il::Instr) throws (binary::Error) {
91 91
    match instr {
92 92
        case il::Instr::Reserve { size, .. } => try value(names, owner, size),
93 93
        case il::Instr::Blit { size, .. } => try value(names, owner, size),
94 94
        case il::Instr::Store { src, .. } => try value(names, owner, src),
95 95
        case il::Instr::Copy { val, .. } => try value(names, owner, val),
141 141
             },
142 142
    }
143 143
}
144 144
145 145
/// Check that a definition belongs to the selected package.
146 -
unsafe fn definition(names: &mut Names, owner: *[u8], name: *[u8]) throws (binary::Error) {
146 +
fn definition 'tables (names: &mut Names 'tables, owner: *[u8], name: *[u8]) throws (binary::Error) {
147 147
    let rest = mem::stripPrefix(owner, name) else {
148 148
        throw binary::Error::Invalid;
149 149
    };
150 150
    if rest.len < 3 or rest[0] <> ':' or rest[1] <> ':' {
151 151
        throw binary::Error::Invalid;
153 153
    try add(names, name);
154 154
}
155 155
156 156
/// Build package tables from local definitions and their qualified references.
157 157
/// Returned tables borrow `names`. Reset the collector before building another package.
158 -
export unsafe fn package(
159 -
    names: &mut Names,
158 +
export unsafe fn package 'tables (
159 +
    names: &mut Names 'tables,
160 160
    owner: *[u8],
161 161
    program: il::Program,
162 162
    exports: &[binary::Export],
163 163
    entry: ?*[u8]
164 164
) -> binary::Package throws (binary::Error) {
lib/std/lang/il/binary/decodeTests.rad +38 -30
305 305
    }
306 306
}
307 307
308 308
/// Check symbol collection capacity and package ownership failures.
309 309
@test unsafe fn collectionBounds() throws (testing::TestError) {
310 -
    let mut symbols: [*[u8]; 8] = undefined;
311 -
    let mut dependencies: [*[u8]; 1] = undefined;
310 +
    let mut symbols: [*[u8]; 8] = [""; 8];
311 +
    let mut dependencies: [*[u8]; 1] = [""; 1];
312 312
    let empty = il::Program { data: &[], fns: &[] };
313 -
    let mut names = collect::new(&mut symbols[..0], &mut dependencies[..]);
314 -
    let mut failed = false;
315 -
    try collect::package(&mut names, "p", empty, &[], nil) catch err {
316 -
        try testing::expect(err == binary::Error::Capacity);
317 -
        set failed = true;
318 -
    };
319 -
    try testing::expect(failed);
313 +
    let symbolTable: 'empty = &mut symbols[..0], dependencyTable = &mut dependencies[..] in {
314 +
        let mut names = collect::new(symbolTable, dependencyTable);
315 +
        let mut failed = false;
316 +
        try collect::package(&mut names, "p", empty, &[], nil) catch err {
317 +
            try testing::expect(err == binary::Error::Capacity);
318 +
            set failed = true;
319 +
        };
320 +
        try testing::expect(failed);
321 +
    }
320 322
    let item = il::Data {
321 323
        name: "p::data", size: 8, alignment: 8, readOnly: false, isZeroInit: false,
322 324
        values: &[il::DataValue { item: il::DataItem::Fn("dep::fn"), count: 1 }],
323 325
    };
324 326
    let local = il::Program { data: retainData(&[item]), fns: &[] };
325 -
    set names = collect::new(&mut symbols[..], &mut dependencies[..0]);
326 -
    set failed = false;
327 -
    try collect::package(&mut names, "p", local, &[], nil) catch err {
328 -
        try testing::expect(err == binary::Error::Capacity);
329 -
        set failed = true;
330 -
    };
331 -
    try testing::expect(failed);
332 -
    set names = collect::new(&mut symbols[..], &mut dependencies[..]);
333 -
    set failed = false;
334 -
    try collect::package(&mut names, "other", local, &[], nil) catch err {
335 -
        try testing::expect(err == binary::Error::Invalid);
336 -
        set failed = true;
337 -
    };
338 -
    try testing::expect(failed);
339 -
    set names = collect::new(&mut symbols[..], &mut dependencies[..]);
340 -
    let package = try collect::package(&mut names, "p", local, &[], nil) catch {
341 -
        throw testing::TestError::Failed;
342 -
    };
343 -
    try testing::expect(package.dependencies.len == 1);
344 -
    try testing::expectBytesEq(package.dependencies[0], "dep");
345 -
    try testing::expect(package.symbols.len == 4);
327 +
    let symbolTable: 'capacity = &mut symbols[..], dependencyTable = &mut dependencies[..0] in {
328 +
        let mut names = collect::new(symbolTable, dependencyTable);
329 +
        let mut failed = false;
330 +
        try collect::package(&mut names, "p", local, &[], nil) catch err {
331 +
            try testing::expect(err == binary::Error::Capacity);
332 +
            set failed = true;
333 +
        };
334 +
        try testing::expect(failed);
335 +
    }
336 +
    let symbolTable: 'owner = &mut symbols[..], dependencyTable = &mut dependencies[..] in {
337 +
        let mut names = collect::new(symbolTable, dependencyTable);
338 +
        let mut failed = false;
339 +
        try collect::package(&mut names, "other", local, &[], nil) catch err {
340 +
            try testing::expect(err == binary::Error::Invalid);
341 +
            set failed = true;
342 +
        };
343 +
        try testing::expect(failed);
344 +
    }
345 +
    let symbolTable: 'valid = &mut symbols[..], dependencyTable = &mut dependencies[..] in {
346 +
        let mut names = collect::new(symbolTable, dependencyTable);
347 +
        let package = try collect::package(&mut names, "p", local, &[], nil) catch {
348 +
            throw testing::TestError::Failed;
349 +
        };
350 +
        try testing::expect(package.dependencies.len == 1);
351 +
        try testing::expectBytesEq(package.dependencies[0], "dep");
352 +
        try testing::expect(package.symbols.len == 4);
353 +
    }
346 354
}
347 355
348 356
/// Copy data fixtures into stable storage for the package descriptor.
349 357
unsafe fn retainData(items: &[il::Data]) -> *[il::Data] {
350 358
    unsafe static DATA: [il::Data; 2] = undefined;