compiler/
kernel/
lib/
examples/
std/
arch/
char/
collections/
lang/
alloc/
ast/
gen/
il/
binary/
collect.rad
7.2 KiB
decodeTests.rad
14.9 KiB
program.rad
10.9 KiB
reader.rad
14.1 KiB
tests.rad
13.7 KiB
writer.rad
12.7 KiB
binary.rad
5.9 KiB
printer.rad
15.4 KiB
tests.rad
3.8 KiB
module/
parser/
resolver/
scanner/
alloc.rad
7.1 KiB
ast.rad
26.7 KiB
gen.rad
513 B
il.rad
19.5 KiB
lower.rad
308.1 KiB
module.rad
14.9 KiB
package.rad
1.3 KiB
parser.rad
89.5 KiB
resolver.rad
439.6 KiB
scanner.rad
18.0 KiB
sexpr.rad
6.4 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
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
281 B
scripts/
seed/
sublime/
test/
vim/
.gitignore
336 B
.gitsigners
112 B
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
9.2 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
662 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 must outlive the collector and all packages that use them. |
| 9 | export record Names: Copy { |
| 10 | /// Unique names in first-use order. |
| 11 | symbols: *unsafe mut [*[u8]], |
| 12 | /// Number of initialized symbol entries. |
| 13 | symbolCount: u32, |
| 14 | /// Unique dependency names in first-use order. |
| 15 | dependencies: *unsafe mut [*[u8]], |
| 16 | /// Number of initialized dependency entries. |
| 17 | dependencyCount: u32, |
| 18 | } |
| 19 | |
| 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 }; |
| 24 | } |
| 25 | |
| 26 | /// Add a unique name and check the symbol table's capacity. |
| 27 | unsafe fn add(names: &mut Names, 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 | unsafe fn reference(names: &mut Names, 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 | unsafe fn value(names: &mut Names, 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 | unsafe fn values(names: &mut Names, 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 | unsafe fn instruction(names: &mut Names, owner: *[u8], instr: il::Instr) throws (binary::Error) { |
| 91 | match instr { |
| 92 | case il::Instr::Reserve { size, .. } => try value(names, owner, size), |
| 93 | case il::Instr::Blit { size, .. } => try value(names, owner, size), |
| 94 | case il::Instr::Store { src, .. } => try value(names, owner, src), |
| 95 | case il::Instr::Copy { val, .. } => try value(names, owner, val), |
| 96 | case il::Instr::Zext { val, .. } => try value(names, owner, val), |
| 97 | case il::Instr::Sext { val, .. } => try value(names, owner, val), |
| 98 | case il::Instr::BinOp { a, b, .. } => { |
| 99 | try value(names, owner, a); |
| 100 | try value(names, owner, b); |
| 101 | }, |
| 102 | case il::Instr::UnOp { a, .. } => try value(names, owner, a), |
| 103 | case il::Instr::Call { func, args, .. } => { |
| 104 | try value(names, owner, func); |
| 105 | try values(names, owner, args); |
| 106 | }, |
| 107 | case il::Instr::Ret { val } => { |
| 108 | if let item = val { |
| 109 | try value(names, owner, item); |
| 110 | } |
| 111 | }, |
| 112 | case il::Instr::Jmp { args, .. } => try values(names, owner, args), |
| 113 | case il::Instr::Br { a, b, thenArgs, elseArgs, .. } => { |
| 114 | try value(names, owner, a); |
| 115 | try value(names, owner, b); |
| 116 | try values(names, owner, thenArgs); |
| 117 | try values(names, owner, elseArgs); |
| 118 | }, |
| 119 | case il::Instr::Switch { val, defaultArgs, cases, .. } => { |
| 120 | try value(names, owner, val); |
| 121 | try values(names, owner, defaultArgs); |
| 122 | for branch in cases { |
| 123 | try values(names, owner, branch.args); |
| 124 | } |
| 125 | }, |
| 126 | case il::Instr::Ecall { num, a0, a1, a2, a3, .. } => { |
| 127 | try value(names, owner, num); |
| 128 | try value(names, owner, a0); |
| 129 | try value(names, owner, a1); |
| 130 | try value(names, owner, a2); |
| 131 | try value(names, owner, a3); |
| 132 | }, |
| 133 | case il::Instr::DeviceRead { handle, offset, .. } => { |
| 134 | try value(names, owner, handle); try value(names, owner, offset); |
| 135 | }, |
| 136 | case il::Instr::DeviceWrite { handle, offset, value: source, .. } => { |
| 137 | try value(names, owner, handle); try value(names, owner, offset); try value(names, owner, source); |
| 138 | }, |
| 139 | case il::Instr::Load { .. }, il::Instr::Sload { .. }, il::Instr::Unreachable, |
| 140 | il::Instr::Ebreak, il::Instr::MemoryFence => { |
| 141 | }, |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | /// Check that a definition belongs to the selected package. |
| 146 | unsafe fn definition(names: &mut Names, owner: *[u8], name: *[u8]) throws (binary::Error) { |
| 147 | let rest = mem::stripPrefix(owner, name) else { |
| 148 | throw binary::Error::Invalid; |
| 149 | }; |
| 150 | if rest.len < 3 or rest[0] <> ':' or rest[1] <> ':' { |
| 151 | throw binary::Error::Invalid; |
| 152 | } |
| 153 | try add(names, name); |
| 154 | } |
| 155 | |
| 156 | /// Build package tables from local definitions and their qualified references. |
| 157 | /// Returned tables borrow `names`. Reset the collector before building another package. |
| 158 | export unsafe fn package( |
| 159 | names: &mut Names, |
| 160 | owner: *[u8], |
| 161 | program: il::Program, |
| 162 | exports: &[binary::Export], |
| 163 | entry: ?*[u8] |
| 164 | ) -> binary::Package throws (binary::Error) { |
| 165 | try add(names, owner); |
| 166 | for item in exports { |
| 167 | try definition(names, owner, item.name); |
| 168 | } |
| 169 | if let name = entry { |
| 170 | try definition(names, owner, name); |
| 171 | } |
| 172 | for item in program.data { |
| 173 | try definition(names, owner, item.name); |
| 174 | for data in item.values { |
| 175 | match data.item { |
| 176 | case il::DataItem::Sym(name) => try reference(names, owner, name), |
| 177 | case il::DataItem::Fn(name) => try reference(names, owner, name), |
| 178 | else => { |
| 179 | }, |
| 180 | } |
| 181 | } |
| 182 | } |
| 183 | for func in program.fns { |
| 184 | try definition(names, owner, func.name); |
| 185 | for block in func.blocks { |
| 186 | for instr in block.instrs { |
| 187 | try instruction(names, owner, instr); |
| 188 | } |
| 189 | } |
| 190 | } |
| 191 | return binary::Package { |
| 192 | symbols: &names.symbols[..names.symbolCount], name: owner, |
| 193 | dependencies: &names.dependencies[..names.dependencyCount], exports: exports as *unsafe [binary::Export], entry, program, |
| 194 | }; |
| 195 | } |