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/program.rad
raw
| 1 | //! Binary package envelopes and IL program reconstruction. |
| 2 | //! Tables occur in this order: symbols, dependencies, exports, data, functions. |
| 3 | //! Block records carry labels, parameters, loop depth, predecessors, instructions. |
| 4 | |
| 5 | use std::lang::il; |
| 6 | use std::lang::il::binary; |
| 7 | use std::lang::il::binary::reader; |
| 8 | use std::lang::il::binary::writer; |
| 9 | use std::lang::alloc; |
| 10 | use std::mem; |
| 11 | |
| 12 | /// Write a counted sequence of typed SSA parameters. |
| 13 | fn writeParams 'buffer (out: &mut writer::Writer 'buffer, params: &[il::Param]) throws (binary::Error) { |
| 14 | try writer::integer(out, params.len as u64, 4); |
| 15 | for param in params { |
| 16 | try writer::integer(out, param.value.n as u64, 4); |
| 17 | try writer::typ(out, param.type); |
| 18 | } |
| 19 | } |
| 20 | |
| 21 | /// Write global data definitions and their initializer sequences. |
| 22 | fn writeData 'buffer (out: &mut writer::Writer 'buffer, items: &[il::Data]) throws (binary::Error) { |
| 23 | try writer::integer(out, items.len as u64, 4); |
| 24 | for item in items { |
| 25 | try writer::symbol(out, item.name); |
| 26 | try writer::integer(out, item.size as u64, 4); |
| 27 | try writer::integer(out, item.alignment as u64, 4); |
| 28 | try writer::integer(out, 1 if item.readOnly else 0, 1); |
| 29 | try writer::integer(out, 1 if item.isZeroInit else 0, 1); |
| 30 | try writer::integer(out, item.values.len as u64, 4); |
| 31 | for value in item.values { |
| 32 | try writer::dataValue(out, value); |
| 33 | } |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | /// Write package identity, symbol tables, exports, and the optional entry. |
| 38 | fn writeHeader 'buffer ( |
| 39 | out: &mut writer::Writer 'buffer, |
| 40 | symbols: &[*[u8]], |
| 41 | name: *[u8], |
| 42 | dependencies: &[*[u8]], |
| 43 | exports: &[binary::Export], |
| 44 | entry: ?*[u8] |
| 45 | ) throws (binary::Error) { |
| 46 | try writer::integer(out, binary::MAGIC as u64, 4); |
| 47 | try writer::integer(out, binary::VERSION as u64, 4); |
| 48 | try writer::integer(out, symbols.len as u64, 4); |
| 49 | for name in symbols { |
| 50 | try writer::bytes(out, name); |
| 51 | } |
| 52 | try writer::symbol(out, name); |
| 53 | try writer::integer(out, dependencies.len as u64, 4); |
| 54 | for name in dependencies { |
| 55 | try writer::symbol(out, name); |
| 56 | } |
| 57 | try writer::integer(out, exports.len as u64, 4); |
| 58 | for item in exports { |
| 59 | try writer::symbol(out, item.name); |
| 60 | match item.kind { |
| 61 | case binary::ExportKind::Function => try writer::integer(out, 0, 1), |
| 62 | case binary::ExportKind::Data => try writer::integer(out, 1, 1), |
| 63 | } |
| 64 | } |
| 65 | if let entry = entry { |
| 66 | try writer::integer(out, 1, 1); |
| 67 | try writer::symbol(out, entry); |
| 68 | } else { |
| 69 | try writer::integer(out, 0, 1); |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | /// Write a package. Return the number of encoded bytes. |
| 74 | /// All package tables and IL storage must remain valid during encoding. |
| 75 | export unsafe fn encode(bytes: &mut [u8], package: &binary::Package) -> u32 throws (binary::Error) { |
| 76 | let output: 'output = &mut bytes[..], symbols = &package.symbols[..] in { |
| 77 | let mut out = writer::new(output, symbols); |
| 78 | try writeHeader(&mut out, symbols, package.name, package.dependencies, package.exports, package.entry); |
| 79 | try writeData(&mut out, &package.program.data[..]); |
| 80 | try writeFunctions(&mut out, package.program.fns); |
| 81 | return out.offset; |
| 82 | } |
| 83 | } |
| 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 | |
| 141 | /// Read typed SSA parameters with checked register indices. |
| 142 | unsafe fn readParams 'input (input: &mut reader::Reader 'input) -> *unsafe [il::Param] throws (binary::Error) { |
| 143 | let n = try reader::count(input, 5); |
| 144 | let params = try reader::storage(input, @sizeOf(il::Param), @alignOf(il::Param), n) |
| 145 | as *mut [il::Param]; |
| 146 | let initialized = try fillParams(input, params); |
| 147 | return (&initialized[..]) as *unsafe [il::Param]; |
| 148 | } |
| 149 | |
| 150 | /// Fill owned parameter storage from checked register indices and type tags. |
| 151 | fn fillParams 'input (input: &mut reader::Reader 'input, params: *mut [il::Param]) -> *[il::Param] throws (binary::Error) { |
| 152 | for i in 0..params.len { |
| 153 | let value = try reader::reg(input); |
| 154 | let t = try reader::typ(input); |
| 155 | set params[i] = il::Param { value, type: t }; |
| 156 | } |
| 157 | return params; |
| 158 | } |
| 159 | |
| 160 | /// Extend an initializer extent without exceeding its declared storage. |
| 161 | fn extendDataExtent(extent: u64, value: il::DataValue, size: u32) -> u64 throws (binary::Error) { |
| 162 | let mut width: u32 = 0; |
| 163 | match value.item { |
| 164 | case il::DataItem::Val { typ, .. } => { |
| 165 | set width = il::typeSize(typ); |
| 166 | }, |
| 167 | case il::DataItem::Sym(_), il::DataItem::Fn(_) => { |
| 168 | set width = 8; |
| 169 | }, |
| 170 | case il::DataItem::Str(text) => { |
| 171 | set width = text.len; |
| 172 | }, |
| 173 | case il::DataItem::Undef => { |
| 174 | set width = 1; |
| 175 | }, |
| 176 | } |
| 177 | let amount = width as u64 * value.count as u64; |
| 178 | if extent > size as u64 or amount > size as u64 - extent { |
| 179 | throw binary::Error::Invalid; |
| 180 | } |
| 181 | return extent + amount; |
| 182 | } |
| 183 | |
| 184 | /// Read global data and check initializer extents against declared storage. |
| 185 | unsafe fn readData 'input (input: &mut reader::Reader 'input) -> *[il::Data] throws (binary::Error) { |
| 186 | let n = try reader::count(input, 18); |
| 187 | let items = try reader::storage(input, @sizeOf(il::Data), @alignOf(il::Data), n) |
| 188 | as *mut [il::Data]; |
| 189 | return try fillData(input, items); |
| 190 | } |
| 191 | |
| 192 | /// Fill owned data definitions with checked metadata and initializer extents. |
| 193 | fn fillData 'input (input: &mut reader::Reader 'input, items: *mut [il::Data]) -> *[il::Data] throws (binary::Error) { |
| 194 | for i in 0..items.len { |
| 195 | let name = try reader::symbol(input); |
| 196 | let size = try reader::integer(input, 4) as u32; |
| 197 | let alignment = try reader::integer(input, 4) as u32; |
| 198 | if alignment == 0 or (alignment & (alignment - 1)) <> 0 { |
| 199 | throw binary::Error::Invalid; |
| 200 | } |
| 201 | let readOnly = try reader::flag(input); |
| 202 | let isZeroInit = try reader::flag(input); |
| 203 | let count = try reader::count(input, 5); |
| 204 | unsafe { |
| 205 | let values = try reader::storage(input, @sizeOf(il::DataValue), @alignOf(il::DataValue), count) |
| 206 | as *mut [il::DataValue]; |
| 207 | let initialized = try fillDataValues(input, values, size); |
| 208 | set items[i] = il::Data { name, size, alignment, readOnly, isZeroInit, values: initialized }; |
| 209 | } |
| 210 | } |
| 211 | return items; |
| 212 | } |
| 213 | |
| 214 | /// Fill owned initializer storage within its declared byte extent. |
| 215 | fn fillDataValues 'input (input: &mut reader::Reader 'input, values: *mut [il::DataValue], size: u32) |
| 216 | -> *[il::DataValue] throws (binary::Error) |
| 217 | { |
| 218 | let mut extent: u64 = 0; |
| 219 | for i in 0..values.len { |
| 220 | unsafe { |
| 221 | set values[i] = try reader::dataValue(input); |
| 222 | } |
| 223 | set extent = try extendDataExtent(extent, values[i], size); |
| 224 | } |
| 225 | return values; |
| 226 | } |
| 227 | |
| 228 | /// Read functions with checked block and register indices. |
| 229 | unsafe fn readFunctions 'input (input: &mut reader::Reader 'input, limits: binary::Limits) |
| 230 | -> *unsafe [*unsafe il::Fn] throws (binary::Error) |
| 231 | { |
| 232 | let n = try reader::count(input, 14); |
| 233 | let fns = try reader::storage(input, @sizeOf(*il::Fn), @alignOf(*il::Fn), n) as *mut [*unsafe il::Fn]; |
| 234 | set input.registers = limits.registers; |
| 235 | for i in 0..n { |
| 236 | let name = try reader::symbol(input); |
| 237 | let returnType = try reader::typ(input); |
| 238 | let isExtern = try reader::flag(input); |
| 239 | let params = try readParams(input); |
| 240 | let count = try reader::count(input, 20); |
| 241 | if count > limits.blocks or (isExtern and count <> 0) { |
| 242 | throw binary::Error::Invalid; |
| 243 | } |
| 244 | set input.blocks = count; |
| 245 | let blocks = try reader::storage(input, @sizeOf(il::Block), @alignOf(il::Block), count) |
| 246 | as *mut [il::Block]; |
| 247 | let mut isLeaf = true; |
| 248 | for j in 0..count { |
| 249 | let label = try reader::bytes(input); |
| 250 | let blockParams = try readParams(input); |
| 251 | let loopDepth = try reader::integer(input, 4) as u32; |
| 252 | let predCount = try reader::count(input, 4); |
| 253 | let predStorage = try reader::storage(input, @sizeOf(u32), @alignOf(u32), predCount) as *mut [u32]; |
| 254 | let preds = try fillPredecessors(input, predStorage); |
| 255 | let instrCount = try reader::count(input, 1); |
| 256 | let instrs = try reader::storage(input, @sizeOf(il::Instr), @alignOf(il::Instr), instrCount) |
| 257 | as *mut [il::Instr]; |
| 258 | if try fillInstructions(input, &mut instrs[..]) { |
| 259 | set isLeaf = false; |
| 260 | } |
| 261 | set blocks[j] = il::Block { |
| 262 | label, params: blockParams, instrs: (&mut instrs[..]) as *unsafe mut [il::Instr], locs: &[], preds: (&preds[..]) as *unsafe [u32], loopDepth, |
| 263 | }; |
| 264 | } |
| 265 | let func = try reader::storage(input, @sizeOf(il::Fn), @alignOf(il::Fn), 1) as *mut [il::Fn]; |
| 266 | set func[0] = il::Fn { name, params, returnType, isExtern, isLeaf, blocks: (&blocks[..]) as *unsafe [il::Block] }; |
| 267 | set fns[i] = &func[0]; |
| 268 | } |
| 269 | return (&fns[..]) as *unsafe [*unsafe il::Fn]; |
| 270 | } |
| 271 | |
| 272 | /// Fill owned predecessor storage with checked block indices. |
| 273 | fn fillPredecessors 'input (input: &mut reader::Reader 'input, preds: *mut [u32]) -> *[u32] throws (binary::Error) { |
| 274 | for i in 0..preds.len { |
| 275 | set preds[i] = try reader::target(input); |
| 276 | } |
| 277 | return preds; |
| 278 | } |
| 279 | |
| 280 | /// Fill an instruction table and report whether it contains a call. |
| 281 | fn fillInstructions 'input (input: &mut reader::Reader 'input, instrs: &mut [il::Instr]) -> bool throws (binary::Error) { |
| 282 | let mut hasCall = false; |
| 283 | for i in 0..instrs.len { |
| 284 | unsafe { |
| 285 | set instrs[i] = try reader::instr(input); |
| 286 | } |
| 287 | if il::isCall(instrs[i]) { |
| 288 | set hasCall = true; |
| 289 | } |
| 290 | } |
| 291 | return hasCall; |
| 292 | } |
| 293 | |
| 294 | /// Read the package tables. The input must contain exactly one package. |
| 295 | unsafe fn readPackage 'input (input: &mut reader::Reader 'input, limits: binary::Limits) |
| 296 | -> binary::Package throws (binary::Error) |
| 297 | { |
| 298 | let magic = try reader::integer(input, 4) as u32; |
| 299 | let version = try reader::integer(input, 4) as u32; |
| 300 | if magic <> binary::MAGIC or version <> binary::VERSION { |
| 301 | throw binary::Error::Invalid; |
| 302 | } |
| 303 | let symbolCount = try reader::count(input, 4); |
| 304 | let symbolStorage = try reader::storage(input, @sizeOf(*[u8]), @alignOf(*[u8]), symbolCount) |
| 305 | as *mut [*[u8]]; |
| 306 | let symbols = try fillSymbols(input, symbolStorage); |
| 307 | let source: 'tables = &input.bytes[..], names = &symbols[..] where 'input: 'tables in { |
| 308 | let mut cursor = reader::new(source, input.arena, names); |
| 309 | set cursor.offset = input.offset; |
| 310 | let name = try reader::symbol(&mut cursor); |
| 311 | let depCount = try reader::count(&mut cursor, 4); |
| 312 | let dependencyStorage = try reader::storage(&mut cursor, @sizeOf(*[u8]), @alignOf(*[u8]), depCount) |
| 313 | as *mut [*[u8]]; |
| 314 | let dependencies = try fillDependencies(&mut cursor, dependencyStorage); |
| 315 | let exportCount = try reader::count(&mut cursor, 5); |
| 316 | let exportStorage = try reader::storage(&mut cursor, @sizeOf(binary::Export), @alignOf(binary::Export), exportCount) |
| 317 | as *mut [binary::Export]; |
| 318 | let exports = try fillExports(&mut cursor, exportStorage); |
| 319 | let mut entry: ?*[u8] = nil; |
| 320 | if try reader::flag(&mut cursor) { |
| 321 | set entry = try reader::symbol(&mut cursor); |
| 322 | } |
| 323 | let data = try readData(&mut cursor); |
| 324 | let fns = try readFunctions(&mut cursor, limits); |
| 325 | if cursor.offset <> cursor.bytes.len { |
| 326 | throw binary::Error::Invalid; |
| 327 | } |
| 328 | set input.offset = cursor.offset; |
| 329 | return binary::Package { symbols: (&symbols[..]) as *unsafe [*[u8]], name, dependencies: (&dependencies[..]) as *unsafe [*[u8]], exports: (&exports[..]) as *unsafe [binary::Export], entry, program: il::Program { data, fns } }; |
| 330 | } |
| 331 | } |
| 332 | |
| 333 | /// Fill owned symbol storage with distinct nonempty names. |
| 334 | fn fillSymbols 'input (input: &mut reader::Reader 'input, symbols: *mut [*[u8]]) -> *[*[u8]] throws (binary::Error) { |
| 335 | for i in 0..symbols.len { |
| 336 | unsafe { |
| 337 | set symbols[i] = try reader::bytes(input); |
| 338 | } |
| 339 | let name = symbols[i]; |
| 340 | if name.len == 0 { |
| 341 | throw binary::Error::Invalid; |
| 342 | } |
| 343 | for j in 0..i { |
| 344 | if mem::eq(name, symbols[j]) { |
| 345 | throw binary::Error::Invalid; |
| 346 | } |
| 347 | } |
| 348 | } |
| 349 | return symbols; |
| 350 | } |
| 351 | |
| 352 | /// Fill owned dependency storage from checked symbol indices. |
| 353 | fn fillDependencies 'input (input: &mut reader::Reader 'input, dependencies: *mut [*[u8]]) -> *[*[u8]] throws (binary::Error) { |
| 354 | for i in 0..dependencies.len { |
| 355 | set dependencies[i] = try reader::symbol(input); |
| 356 | } |
| 357 | return dependencies; |
| 358 | } |
| 359 | |
| 360 | /// Fill owned exports from checked symbol indices and kind flags. |
| 361 | fn fillExports 'input (input: &mut reader::Reader 'input, exports: *mut [binary::Export]) -> *[binary::Export] throws (binary::Error) { |
| 362 | for i in 0..exports.len { |
| 363 | let symbol = try reader::symbol(input); |
| 364 | let kind = binary::ExportKind::Data if try reader::flag(input) else binary::ExportKind::Function; |
| 365 | set exports[i] = binary::Export { name: symbol, kind }; |
| 366 | } |
| 367 | return exports; |
| 368 | } |
| 369 | |
| 370 | /// Decode one package. Restore the arena offset on every failure. |
| 371 | /// The caller must retain the decoded allocations until the last package use. |
| 372 | export unsafe fn decode(bytes: &[u8], arena: &mut alloc::Arena, limits: binary::Limits) |
| 373 | -> binary::Package throws (binary::Error) |
| 374 | { |
| 375 | let saved = alloc::save(arena); |
| 376 | let namesTable: [*[u8]; 0] = []; |
| 377 | let source: 'input = &bytes[..], names = &namesTable[..] in { |
| 378 | let mut input = reader::new(source, &mut *arena, names); |
| 379 | return try readPackage(&mut input, limits) catch err { |
| 380 | alloc::restore(arena, saved); |
| 381 | throw err; |
| 382 | }; |
| 383 | } |
| 384 | } |