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.8 KiB
reader.rad
14.0 KiB
tests.rad
13.4 KiB
writer.rad
12.7 KiB
binary.rad
5.9 KiB
printer.rad
15.4 KiB
module/
parser/
resolver/
scanner/
alloc.rad
5.3 KiB
ast.rad
23.6 KiB
gen.rad
513 B
il.rad
17.0 KiB
lower.rad
278.5 KiB
module.rad
13.5 KiB
package.rad
1.3 KiB
parser.rad
79.6 KiB
resolver.rad
329.1 KiB
scanner.rad
17.6 KiB
sexpr.rad
6.4 KiB
strings.rad
2.2 KiB
types.rad
286 B
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
10.2 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
551 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 | /// Collect names from an instruction's variable-length operand sequence. |
| 82 | unsafe fn values(names: &mut Names, owner: *[u8], items: &[il::Val]) throws (binary::Error) { |
| 83 | for item in items { |
| 84 | try value(names, owner, item); |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | /// Collect all symbolic instruction operands. |
| 89 | unsafe fn instruction(names: &mut Names, owner: *[u8], instr: il::Instr) throws (binary::Error) { |
| 90 | match instr { |
| 91 | case il::Instr::Reserve { size, .. } => try value(names, owner, size), |
| 92 | case il::Instr::Blit { size, .. } => try value(names, owner, size), |
| 93 | case il::Instr::Store { src, .. } => try value(names, owner, src), |
| 94 | case il::Instr::Copy { val, .. } => try value(names, owner, val), |
| 95 | case il::Instr::Zext { val, .. } => try value(names, owner, val), |
| 96 | case il::Instr::Sext { val, .. } => try value(names, owner, val), |
| 97 | case il::Instr::BinOp { a, b, .. } => { |
| 98 | try value(names, owner, a); |
| 99 | try value(names, owner, b); |
| 100 | }, |
| 101 | case il::Instr::UnOp { a, .. } => try value(names, owner, a), |
| 102 | case il::Instr::Call { func, args, .. } => { |
| 103 | try value(names, owner, func); |
| 104 | try values(names, owner, args); |
| 105 | }, |
| 106 | case il::Instr::Ret { val } => { |
| 107 | if let item = val { |
| 108 | try value(names, owner, item); |
| 109 | } |
| 110 | }, |
| 111 | case il::Instr::Jmp { args, .. } => try values(names, owner, args), |
| 112 | case il::Instr::Br { a, b, thenArgs, elseArgs, .. } => { |
| 113 | try value(names, owner, a); |
| 114 | try value(names, owner, b); |
| 115 | try values(names, owner, thenArgs); |
| 116 | try values(names, owner, elseArgs); |
| 117 | }, |
| 118 | case il::Instr::Switch { val, defaultArgs, cases, .. } => { |
| 119 | try value(names, owner, val); |
| 120 | try values(names, owner, defaultArgs); |
| 121 | for branch in cases { |
| 122 | try values(names, owner, branch.args); |
| 123 | } |
| 124 | }, |
| 125 | case il::Instr::Ecall { num, a0, a1, a2, a3, .. } => { |
| 126 | try value(names, owner, num); |
| 127 | try value(names, owner, a0); |
| 128 | try value(names, owner, a1); |
| 129 | try value(names, owner, a2); |
| 130 | try value(names, owner, a3); |
| 131 | }, |
| 132 | case il::Instr::DeviceRead { handle, offset, .. } => { |
| 133 | try value(names, owner, handle); try value(names, owner, offset); |
| 134 | }, |
| 135 | case il::Instr::DeviceWrite { handle, offset, value: source, .. } => { |
| 136 | try value(names, owner, handle); try value(names, owner, offset); try value(names, owner, source); |
| 137 | }, |
| 138 | case il::Instr::Load { .. }, il::Instr::Sload { .. }, il::Instr::Unreachable, |
| 139 | il::Instr::Ebreak, il::Instr::MemoryFence => {}, |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | /// Check that a definition belongs to the selected package. |
| 144 | unsafe fn definition(names: &mut Names, owner: *[u8], name: *[u8]) throws (binary::Error) { |
| 145 | let rest = mem::stripPrefix(owner, name) else { |
| 146 | throw binary::Error::Invalid; |
| 147 | }; |
| 148 | if rest.len < 3 or rest[0] <> ':' or rest[1] <> ':' { |
| 149 | throw binary::Error::Invalid; |
| 150 | } |
| 151 | try add(names, name); |
| 152 | } |
| 153 | |
| 154 | /// Build package tables from local definitions and their qualified references. |
| 155 | /// Returned tables borrow `names`. Reset the collector before building another package. |
| 156 | export unsafe fn package( |
| 157 | names: &mut Names, |
| 158 | owner: *[u8], |
| 159 | program: il::Program, |
| 160 | exports: &[binary::Export], |
| 161 | entry: ?*[u8] |
| 162 | ) -> binary::Package throws (binary::Error) { |
| 163 | try add(names, owner); |
| 164 | for item in exports { |
| 165 | try definition(names, owner, item.name); |
| 166 | } |
| 167 | if let name = entry { |
| 168 | try definition(names, owner, name); |
| 169 | } |
| 170 | for item in program.data { |
| 171 | try definition(names, owner, item.name); |
| 172 | for data in item.values { |
| 173 | match data.item { |
| 174 | case il::DataItem::Sym(name) => try reference(names, owner, name), |
| 175 | case il::DataItem::Fn(name) => try reference(names, owner, name), |
| 176 | else => {}, |
| 177 | } |
| 178 | } |
| 179 | } |
| 180 | for func in program.fns { |
| 181 | try definition(names, owner, func.name); |
| 182 | for block in func.blocks { |
| 183 | for instr in block.instrs { |
| 184 | try instruction(names, owner, instr); |
| 185 | } |
| 186 | } |
| 187 | } |
| 188 | return binary::Package { |
| 189 | symbols: &names.symbols[..names.symbolCount], name: owner, |
| 190 | dependencies: &names.dependencies[..names.dependencyCount], exports: exports as *unsafe [binary::Export], entry, program, |
| 191 | }; |
| 192 | } |