compiler/
lib/
examples/
std/
arch/
char/
collections/
graph/
lang/
alloc/
ast/
gen/
il/
binary/
collect.rad
9.5 KiB
decodeTests.rad
32.7 KiB
program.rad
15.3 KiB
reader.rad
17.2 KiB
tests.rad
28.0 KiB
writer.rad
13.9 KiB
binary.rad
5.9 KiB
printer.rad
16.0 KiB
published.rad
13.4 KiB
publishedTests.rad
8.7 KiB
tests.rad
14.7 KiB
module/
parser/
resolver/
scanner/
alloc.rad
7.1 KiB
ast.rad
26.9 KiB
gen.rad
513 B
il.rad
20.4 KiB
lower.rad
321.7 KiB
module.rad
17.3 KiB
package.rad
1.3 KiB
parser.rad
92.2 KiB
resolver.rad
511.1 KiB
scanner.rad
17.9 KiB
sexpr.rad
6.7 KiB
strings.rad
2.2 KiB
types.rad
1.6 KiB
sys/
arch.rad
68 B
char.rad
855 B
collections.rad
39 B
fmt.rad
8.3 KiB
graph.rad
4.3 KiB
intrinsics.rad
467 B
io.rad
1.7 KiB
lang.rad
276 B
mem.rad
2.3 KiB
sys.rad
179 B
testing.rad
2.4 KiB
tests.rad
15.7 KiB
vec.rad
3.2 KiB
std.rad
299 B
scripts/
seed/
sublime/
test/
vim/
.gitignore
336 B
.gitsigners
112 B
CELL_PERMISSIONS
6.8 KiB
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
5.4 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
808 B
lib/std/lang/il/binary/collect.rad
raw
| 1 | //! Deterministic symbol and dependency collection for package-local IL. |
| 2 | |
| 3 | use std::mem; |
| 4 | use std::lang::il; |
| 5 | use std::lang::il::binary; |
| 6 | |
| 7 | /// Caller-owned storage for a package's indexed names. |
| 8 | /// Both tables are exclusively borrowed for the collector's region. |
| 9 | export record Names: 'tables { |
| 10 | /// Unique names in first-use order. |
| 11 | symbols: &'tables mut [*[u8]], |
| 12 | /// Number of initialized symbol entries. |
| 13 | symbolCount: u32, |
| 14 | /// Unique dependency names in first-use order. |
| 15 | dependencies: &'tables mut [*[u8]], |
| 16 | /// Number of initialized dependency entries. |
| 17 | dependencyCount: u32, |
| 18 | } |
| 19 | |
| 20 | /// Start collection with empty caller-provided tables. |
| 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 | } |
| 25 | |
| 26 | /// Add a unique name and check the symbol table's capacity. |
| 27 | fn add 'tables (names: &mut Names 'tables, name: *[u8]) throws (binary::Error) { |
| 28 | if name.len == 0 { |
| 29 | throw binary::Error::Invalid; |
| 30 | } |
| 31 | for i in 0..names.symbolCount { |
| 32 | if mem::eq(names.symbols[i], name) { |
| 33 | return; |
| 34 | } |
| 35 | } |
| 36 | if names.symbolCount == names.symbols.len { |
| 37 | throw binary::Error::Capacity; |
| 38 | } |
| 39 | set names.symbols[names.symbolCount] = name; |
| 40 | set names.symbolCount += 1; |
| 41 | } |
| 42 | |
| 43 | /// Add a qualified reference and its owning package dependency. |
| 44 | fn reference 'tables (names: &mut Names 'tables, owner: *[u8], name: *[u8]) throws (binary::Error) { |
| 45 | try add(names, name); |
| 46 | for i in 0..name.len { |
| 47 | if name[i] == ':' and i + 1 < name.len and name[i + 1] == ':' { |
| 48 | if i == 0 or i + 2 == name.len { |
| 49 | throw binary::Error::Invalid; |
| 50 | } |
| 51 | let dependency = &name[..i]; |
| 52 | if mem::eq(dependency, owner) { |
| 53 | return; |
| 54 | } |
| 55 | try add(names, dependency); |
| 56 | for j in 0..names.dependencyCount { |
| 57 | if mem::eq(names.dependencies[j], dependency) { |
| 58 | return; |
| 59 | } |
| 60 | } |
| 61 | if names.dependencyCount == names.dependencies.len { |
| 62 | throw binary::Error::Capacity; |
| 63 | } |
| 64 | set names.dependencies[names.dependencyCount] = dependency; |
| 65 | set names.dependencyCount += 1; |
| 66 | return; |
| 67 | } |
| 68 | } |
| 69 | throw binary::Error::Invalid; |
| 70 | } |
| 71 | |
| 72 | /// Collect names referenced by one IL value. |
| 73 | fn value 'tables (names: &mut Names 'tables, owner: *[u8], item: il::Val) throws (binary::Error) { |
| 74 | match item { |
| 75 | case il::Val::DataSym(name) => try reference(names, owner, name), |
| 76 | case il::Val::FnAddr(name) => try reference(names, owner, name), |
| 77 | else => { |
| 78 | }, |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | /// Collect names from an instruction's variable-length operand sequence. |
| 83 | fn values 'tables (names: &mut Names 'tables, owner: *[u8], items: &[il::Val]) throws (binary::Error) { |
| 84 | for item in items { |
| 85 | try value(names, owner, item); |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | /// Collect all symbolic instruction operands. |
| 90 | fn instruction 'tables (names: &mut Names 'tables, owner: *[u8], instr: &il::Instr) throws (binary::Error) { |
| 91 | match instr { |
| 92 | case il::Instr::Call { func, args, .. } => { |
| 93 | try value(names, owner, *func); |
| 94 | unsafe { |
| 95 | try values(names, owner, *args); |
| 96 | } |
| 97 | }, |
| 98 | case il::Instr::Jmp { args, .. } => { |
| 99 | unsafe { |
| 100 | try values(names, owner, *args); |
| 101 | } |
| 102 | }, |
| 103 | case il::Instr::Br { a, b, thenArgs, elseArgs, .. } => { |
| 104 | try value(names, owner, *a); |
| 105 | try value(names, owner, *b); |
| 106 | unsafe { |
| 107 | try values(names, owner, *thenArgs); |
| 108 | try values(names, owner, *elseArgs); |
| 109 | } |
| 110 | }, |
| 111 | case il::Instr::Switch { val, defaultArgs, cases, .. } => { |
| 112 | try value(names, owner, *val); |
| 113 | unsafe { |
| 114 | try switchValues(names, owner, *defaultArgs, *cases); |
| 115 | } |
| 116 | }, |
| 117 | else => try fixedInstruction(names, owner, instr), |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | /// Collect default arguments before the arguments of each switch case. |
| 122 | fn switchValues 'tables (names: &mut Names 'tables, owner: *[u8], defaultArgs: &[il::Val], cases: &[il::SwitchCase]) throws (binary::Error) { |
| 123 | try values(names, owner, defaultArgs); |
| 124 | for i in 0..cases.len { |
| 125 | let branch = &cases[i]; |
| 126 | unsafe { |
| 127 | try values(names, owner, branch.args); |
| 128 | } |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | /// Collect symbolic operands stored directly in an instruction. |
| 133 | fn fixedInstruction 'tables (names: &mut Names 'tables, owner: *[u8], instr: &il::Instr) throws (binary::Error) { |
| 134 | match *instr { |
| 135 | case il::Instr::Reserve { size, .. } => try value(names, owner, size), |
| 136 | case il::Instr::Blit { size, .. } => try value(names, owner, size), |
| 137 | case il::Instr::Store { src, .. } => try value(names, owner, src), |
| 138 | case il::Instr::Copy { val, .. } => try value(names, owner, val), |
| 139 | case il::Instr::Zext { val, .. } => try value(names, owner, val), |
| 140 | case il::Instr::Sext { val, .. } => try value(names, owner, val), |
| 141 | case il::Instr::BinOp { a, b, .. } => { |
| 142 | try value(names, owner, a); |
| 143 | try value(names, owner, b); |
| 144 | }, |
| 145 | case il::Instr::UnOp { a, .. } => try value(names, owner, a), |
| 146 | case il::Instr::Ret { val } => { |
| 147 | if let item = val { |
| 148 | try value(names, owner, item); |
| 149 | } |
| 150 | }, |
| 151 | case il::Instr::Ecall { num, a0, a1, a2, a3, .. } => { |
| 152 | try value(names, owner, num); |
| 153 | try value(names, owner, a0); |
| 154 | try value(names, owner, a1); |
| 155 | try value(names, owner, a2); |
| 156 | try value(names, owner, a3); |
| 157 | }, |
| 158 | case il::Instr::DeviceRead { handle, offset, .. } => { |
| 159 | try value(names, owner, handle); try value(names, owner, offset); |
| 160 | }, |
| 161 | case il::Instr::DeviceWrite { handle, offset, value: source, .. } => { |
| 162 | try value(names, owner, handle); try value(names, owner, offset); try value(names, owner, source); |
| 163 | }, |
| 164 | case il::Instr::Load { .. }, il::Instr::Sload { .. }, il::Instr::Unreachable, |
| 165 | il::Instr::Ebreak, il::Instr::MemoryFence => { |
| 166 | }, |
| 167 | case il::Instr::Call { .. }, il::Instr::Jmp { .. }, il::Instr::Br { .. }, il::Instr::Switch { .. } => |
| 168 | panic "fixedInstruction: expected inline operands", |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | /// Check that a definition belongs to the selected package. |
| 173 | fn definition 'tables (names: &mut Names 'tables, owner: *[u8], name: *[u8]) throws (binary::Error) { |
| 174 | let rest = mem::stripPrefix(owner, name) else { |
| 175 | throw binary::Error::Invalid; |
| 176 | }; |
| 177 | if rest.len < 3 or rest[0] <> ':' or rest[1] <> ':' { |
| 178 | throw binary::Error::Invalid; |
| 179 | } |
| 180 | try add(names, name); |
| 181 | } |
| 182 | |
| 183 | /// Collect definitions and symbolic initializers from global data. |
| 184 | fn collectData 'tables (names: &mut Names 'tables, owner: *[u8], items: &[il::Data]) throws (binary::Error) { |
| 185 | for item in items { |
| 186 | try definition(names, owner, item.name); |
| 187 | for data in item.values { |
| 188 | match data.item { |
| 189 | case il::DataItem::Sym(name) => try reference(names, owner, name), |
| 190 | case il::DataItem::Fn(name) => try reference(names, owner, name), |
| 191 | else => { |
| 192 | }, |
| 193 | } |
| 194 | } |
| 195 | } |
| 196 | } |
| 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 | |
| 226 | /// Build package tables from local definitions and their qualified references. |
| 227 | /// Returned tables borrow `names`. Reset the collector before building another package. |
| 228 | export unsafe fn package 'tables ( |
| 229 | names: &mut Names 'tables, |
| 230 | owner: *[u8], |
| 231 | program: il::Program, |
| 232 | exports: &[binary::Export], |
| 233 | entry: ?*[u8] |
| 234 | ) -> binary::Package throws (binary::Error) { |
| 235 | try collectNames(names, owner, &program, exports, entry); |
| 236 | return binary::Package { |
| 237 | symbols: &names.symbols[..names.symbolCount], name: owner, |
| 238 | dependencies: &names.dependencies[..names.dependencyCount], exports: exports as *unsafe [binary::Export], entry, program, |
| 239 | }; |
| 240 | } |
| 241 | |
| 242 | /// Collect package metadata before data and function references. |
| 243 | fn collectNames 'tables ( |
| 244 | names: &mut Names 'tables, |
| 245 | owner: *[u8], |
| 246 | program: &il::Program, |
| 247 | exports: &[binary::Export], |
| 248 | entry: ?*[u8], |
| 249 | ) throws (binary::Error) { |
| 250 | try add(names, owner); |
| 251 | for item in exports { |
| 252 | try definition(names, owner, item.name); |
| 253 | } |
| 254 | if let name = entry { |
| 255 | try definition(names, owner, name); |
| 256 | } |
| 257 | try collectData(names, owner, &program.data[..]); |
| 258 | unsafe { |
| 259 | try collectFunctions(names, owner, program.fns); |
| 260 | } |
| 261 | } |