compiler: Check package function and block collection

da81577b49e87052ceae83cef63535b12677388d0072e7a26a056026e2f6f11b
Alexis Sellier committed ago 1 parent a2a9b893
lib/std/lang/il/binary/collect.rad +29 -8
193 193
            }
194 194
        }
195 195
    }
196 196
}
197 197
198 +
/// Collect function definitions and their bodies in pointer-table order.
199 +
fn collectFunctions 'tables (names: &mut Names 'tables, owner: *[u8], functions: &[*unsafe il::Fn]) throws (binary::Error) {
200 +
    for i in 0..functions.len {
201 +
        let func = functions[i];
202 +
        unsafe {
203 +
            try collectFunction(names, owner, func.name, func.blocks);
204 +
        }
205 +
    }
206 +
}
207 +
208 +
/// Collect a function definition before its block references.
209 +
fn collectFunction 'tables (names: &mut Names 'tables, owner: *[u8], name: *[u8], blocks: &[il::Block]) throws (binary::Error) {
210 +
    try definition(names, owner, name);
211 +
    for i in 0..blocks.len {
212 +
        let block = &blocks[i];
213 +
        unsafe {
214 +
            try collectInstructions(names, owner, block.instrs);
215 +
        }
216 +
    }
217 +
}
218 +
219 +
/// Collect instruction references in table order.
220 +
fn collectInstructions 'tables (names: &mut Names 'tables, owner: *[u8], instructions: &[il::Instr]) throws (binary::Error) {
221 +
    for i in 0..instructions.len {
222 +
        try instruction(names, owner, &instructions[i]);
223 +
    }
224 +
}
225 +
198 226
/// Build package tables from local definitions and their qualified references.
199 227
/// Returned tables borrow `names`. Reset the collector before building another package.
200 228
export unsafe fn package 'tables (
201 229
    names: &mut Names 'tables,
202 230
    owner: *[u8],
210 238
    }
211 239
    if let name = entry {
212 240
        try definition(names, owner, name);
213 241
    }
214 242
    try collectData(names, owner, &program.data[..]);
215 -
    for func in program.fns {
216 -
        try definition(names, owner, func.name);
217 -
        for block in func.blocks {
218 -
            for instr in block.instrs {
219 -
                try instruction(names, owner, &instr);
220 -
            }
221 -
        }
222 -
    }
243 +
    try collectFunctions(names, owner, program.fns);
223 244
    return binary::Package {
224 245
        symbols: &names.symbols[..names.symbolCount], name: owner,
225 246
        dependencies: &names.dependencies[..names.dependencyCount], exports: exports as *unsafe [binary::Export], entry, program,
226 247
    };
227 248
}
lib/std/lang/il/binary/decodeTests.rad +39 -0
525 525
        }
526 526
        for i in capacity..symbols.len { assert symbols[i].len == 0; }
527 527
    }
528 528
}
529 529
530 +
/// Function definitions precede their block references in table order.
531 +
@test unsafe fn functionCollectionOrder() throws (testing::TestError) {
532 +
    let external = il::Fn { name: "p::external", params: &[], returnType: il::Type::W64,
533 +
        isExtern: true, isLeaf: true, blocks: &[] };
534 +
    let mut first = [il::Instr::Copy { dst: il::Reg { n: 0 }, val: il::Val::DataSym("z::data") }];
535 +
    let mut second = [il::Instr::Ret { val: il::Val::FnAddr("a::fn") }];
536 +
    let blocks = [
537 +
        il::Block { label: "first", params: &[], instrs: &mut first[..], locs: &[], preds: &[], loopDepth: 0 },
538 +
        il::Block { label: "empty", params: &[], instrs: &mut [], locs: &[], preds: &[], loopDepth: 0 },
539 +
        il::Block { label: "second", params: &[], instrs: &mut second[..], locs: &[], preds: &[], loopDepth: 0 },
540 +
    ];
541 +
    let function = il::Fn { name: "p::main", params: &[], returnType: il::Type::W64,
542 +
        isExtern: false, isLeaf: true, blocks: &blocks[..] };
543 +
    let local = il::Program { data: &[], fns: &[&external, &function] };
544 +
    let expected: [*[u8]; 7] = ["p", "p::external", "p::main", "z::data", "z", "a::fn", "a"];
545 +
    for capacity in 0..expected.len + 1 {
546 +
        let mut symbols: [*[u8]; 8] = [""; 8];
547 +
        let mut dependencies: [*[u8]; 2] = [""; 2];
548 +
        let symbolTable: 'tables = &mut symbols[..capacity], dependencyTable = &mut dependencies[..] in {
549 +
            let mut names = collect::new(symbolTable, dependencyTable);
550 +
            let mut failed = false;
551 +
            try collect::package(&mut names, "p", local, &[], nil) catch err {
552 +
                assert err == binary::Error::Capacity;
553 +
                set failed = true;
554 +
            };
555 +
            assert failed == (capacity < expected.len);
556 +
            assert names.symbolCount <= capacity;
557 +
            if not failed {
558 +
                assert names.symbolCount == expected.len;
559 +
                for name, i in expected { try testing::expectBytesEq(names.symbols[i], name); }
560 +
                assert names.dependencyCount == 2;
561 +
                try testing::expectBytesEq(names.dependencies[0], "z");
562 +
                try testing::expectBytesEq(names.dependencies[1], "a");
563 +
            }
564 +
        }
565 +
        for i in capacity..symbols.len { assert symbols[i].len == 0; }
566 +
    }
567 +
}
568 +
530 569
/// Data collection retains first-use order and unique dependencies.
531 570
@test unsafe fn dataCollectionOrder() throws (testing::TestError) {
532 571
    let item = il::Data {
533 572
        name: "p::data", size: 64, alignment: 8, readOnly: true, isZeroInit: false,
534 573
        values: &[