compiler: Check package function and block collection
da81577b49e87052ceae83cef63535b12677388d0072e7a26a056026e2f6f11b
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: &[ |