compiler: Check binary function and block encoding
81eb8a8d3de0eb33027a238fc87f57d38d9007037de920d5f164eb92e5f0efa7
1 parent
9e618540
lib/std/lang/il/binary/decodeTests.rad
+19 -0
| 49 | 49 | }; |
|
| 50 | 50 | try testing::expectBytesEq(decoded.name, package.name); |
|
| 51 | 51 | try testing::expect(decoded.dependencies.len == package.dependencies.len); |
|
| 52 | 52 | try testing::expect(decoded.exports.len == package.exports.len); |
|
| 53 | 53 | try testing::expect(decoded.program.fns.len == package.program.fns.len); |
|
| 54 | + | for original, i in package.program.fns { |
|
| 55 | + | let restored = decoded.program.fns[i]; |
|
| 56 | + | assert restored.blocks.len == original.blocks.len; |
|
| 57 | + | for block, j in original.blocks { |
|
| 58 | + | let rebuilt = &restored.blocks[j]; |
|
| 59 | + | try testing::expectBytesEq(rebuilt.label, block.label); |
|
| 60 | + | assert rebuilt.loopDepth == block.loopDepth; |
|
| 61 | + | assert rebuilt.params.len == block.params.len; |
|
| 62 | + | for param, k in block.params { |
|
| 63 | + | assert rebuilt.params[k].value.n == param.value.n; |
|
| 64 | + | assert rebuilt.params[k].type == param.type; |
|
| 65 | + | } |
|
| 66 | + | assert rebuilt.preds.len == block.preds.len; |
|
| 67 | + | for pred, k in block.preds { assert rebuilt.preds[k] == pred; } |
|
| 68 | + | } |
|
| 69 | + | } |
|
| 54 | 70 | let mut encoded: [u8; 2048] = [0; 2048]; |
|
| 55 | 71 | let repeated = try program::encode(&mut encoded[..], &decoded) catch { |
|
| 56 | 72 | throw testing::TestError::Failed; |
|
| 57 | 73 | }; |
|
| 58 | 74 | try testing::expectBytesEq(&buffer[..length], &encoded[..repeated]); |
| 135 | 151 | ]; |
|
| 136 | 152 | let func = il::Fn { |
|
| 137 | 153 | name: "p::main", params, returnType: il::Type::W64, isExtern: false, isLeaf: false, |
|
| 138 | 154 | blocks: &[il::Block { |
|
| 139 | 155 | label: "entry", params: &[], instrs: &mut instrs[..], locs: &[], preds: &[0], loopDepth: 2, |
|
| 156 | + | }, il::Block { |
|
| 157 | + | label: "empty", params: &[il::Param { value: il::Reg { n: 3 }, type: il::Type::W32 }], |
|
| 158 | + | instrs: &mut [], locs: &[], preds: &[0, 1], loopDepth: 0, |
|
| 140 | 159 | }], |
|
| 141 | 160 | }; |
|
| 142 | 161 | let external = il::Fn { |
|
| 143 | 162 | name: "dep::fn", params, returnType: il::Type::W64, isExtern: true, isLeaf: true, blocks: &[], |
|
| 144 | 163 | }; |
lib/std/lang/il/binary/program.rad
+57 -21
| 75 | 75 | export unsafe fn encode(bytes: &mut [u8], package: &binary::Package) -> u32 throws (binary::Error) { |
|
| 76 | 76 | let output: 'output = &mut bytes[..], symbols = &package.symbols[..] in { |
|
| 77 | 77 | let mut out = writer::new(output, symbols); |
|
| 78 | 78 | try writeHeader(&mut out, symbols, package.name, package.dependencies, package.exports, package.entry); |
|
| 79 | 79 | try writeData(&mut out, &package.program.data[..]); |
|
| 80 | - | try writer::integer(&mut out, package.program.fns.len as u64, 4); |
|
| 81 | - | for func in package.program.fns { |
|
| 82 | - | try writer::symbol(&mut out, func.name); |
|
| 83 | - | try writer::typ(&mut out, func.returnType); |
|
| 84 | - | try writer::integer(&mut out, 1 if func.isExtern else 0, 1); |
|
| 85 | - | try writeParams(&mut out, func.params); |
|
| 86 | - | try writer::integer(&mut out, func.blocks.len as u64, 4); |
|
| 87 | - | for block in func.blocks { |
|
| 88 | - | try writer::bytes(&mut out, block.label); |
|
| 89 | - | try writeParams(&mut out, block.params); |
|
| 90 | - | try writer::integer(&mut out, block.loopDepth as u64, 4); |
|
| 91 | - | try writer::integer(&mut out, block.preds.len as u64, 4); |
|
| 92 | - | for pred in block.preds { |
|
| 93 | - | try writer::integer(&mut out, pred as u64, 4); |
|
| 94 | - | } |
|
| 95 | - | try writer::integer(&mut out, block.instrs.len as u64, 4); |
|
| 96 | - | for instr in block.instrs { |
|
| 97 | - | try writer::instr(&mut out, instr); |
|
| 98 | - | } |
|
| 99 | - | } |
|
| 100 | - | } |
|
| 80 | + | try writeFunctions(&mut out, package.program.fns); |
|
| 101 | 81 | return out.offset; |
|
| 102 | 82 | } |
|
| 103 | 83 | } |
|
| 104 | 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 | + | ||
| 105 | 141 | /// Read typed SSA parameters with checked register indices. |
|
| 106 | 142 | unsafe fn readParams 'input (input: &mut reader::Reader 'input) -> *unsafe [il::Param] throws (binary::Error) { |
|
| 107 | 143 | let n = try reader::count(input, 5); |
|
| 108 | 144 | let params = try reader::storage(input, @sizeOf(il::Param), @alignOf(il::Param), n) |
|
| 109 | 145 | as *mut [il::Param]; |