compiler: Emit separate binary RIL packages

d1be47b57c298a91a6623c05a13e174ed27cba8175fe5090d9553db0609e2d40
Alexis Sellier committed ago 1 parent eba4a943
compiler/radiance.rad +145 -1
17 17
use std::arch::rv64;
18 18
use std::arch::rv64::asm;
19 19
use std::arch::rv64::printer;
20 20
use std::lang::sexpr;
21 21
use std::lang::gen::data;
22 +
use std::lang::il::binary;
23 +
use std::lang::il::binary::collect;
24 +
use std::lang::il::binary::program;
22 25
use std::lang::gen::types;
23 26
use std::sys;
24 27
use std::sys::unix;
25 28
use std::collections::dict;
26 29
67 70
/// Code generation storage.
68 71
unsafe static CODEGEN_DATA_SYMS: [data::DataSym; data::MAX_DATA_SYMS] = undefined;
69 72
/// Hash table entries for data symbol lookup.
70 73
unsafe static CODEGEN_DATA_SYM_ENTRIES: [dict::Entry; data::DATA_SYM_TABLE_SIZE] = undefined;
71 74
75 +
/// Maximum number of exported definitions in one binary package.
76 +
constant MAX_PACKAGE_EXPORTS: u32 = 8192;
77 +
/// Maximum number of indexed names in one binary package.
78 +
constant MAX_PACKAGE_SYMBOLS: u32 = 16384;
79 +
/// Export workspace reused for each binary package.
80 +
unsafe static PACKAGE_EXPORTS: [binary::Export; MAX_PACKAGE_EXPORTS] = undefined;
81 +
/// Symbol workspace reused for each binary package.
82 +
unsafe static PACKAGE_SYMBOLS: [*[u8]; MAX_PACKAGE_SYMBOLS] = undefined;
83 +
72 84
/// Debug info file extension.
73 85
constant DEBUG_EXT: *[u8] = ".debug";
74 86
75 87
/// Maximum rodata size (4MB).
76 88
constant MAX_RO_DATA_SIZE: u32 = 4194304;
96 108
/// Symbol name exported for startup code to call the semantic entry function.
97 109
export constant DEFAULT_ENTRY_SYMBOL: *[u8] = "::default";
98 110
99 111
/// Usage string.
100 112
constant USAGE: *[u8] =
101 -
    "usage: radiance -pkg <name> [-start <input.ras>] -mod <input>.. [-pkg <name> -mod <input>..] -entry <pkg> -o <output>\n";
113 +
    "usage: radiance -pkg <name> [-start <input.ras>] -mod <input>.. [-pkg <name> -mod <input>..] -entry <pkg> [-o <output> | -ril <directory>]\n";
102 114
103 115
/// Compiler error.
104 116
union Error: Copy {
105 117
    Other,
106 118
}
159 171
    config: resolver::Config,
160 172
    /// What to dump during compilation.
161 173
    dump: Dump,
162 174
    /// Output path for binary.
163 175
    outputPath: ?*[u8],
176 +
    /// Output directory for separate binary RIL packages.
177 +
    rilDirectory: ?*[u8],
164 178
    /// Whether to emit debug info (.debug file).
165 179
    debug: bool,
166 180
}
167 181
168 182
/// Root module info for a package.
293 307
    arena: &mut ast::NodeArena
294 308
) -> CompileContext throws (Error) {
295 309
    let mut buildTest = false;
296 310
    let mut debugEnabled = false;
297 311
    let mut outputPath: ?*[u8] = nil;
312 +
    let mut rilDirectory: ?*[u8] = nil;
298 313
    let mut dump = Dump::None;
299 314
    let mut entryPkgName: ?*[u8] = nil;
300 315
301 316
    // Per-package source path tracking.
302 317
    let mut inputs: [PackageInput; MAX_PACKAGES] = undefined;
359 374
        } else if mem::eq(arg, "-debug") {
360 375
            set debugEnabled = true;
361 376
        } else if mem::eq(arg, "-o") {
362 377
            try nextArg(args, &mut idx, &["`-o` requires an output path"]);
363 378
            set outputPath = args[idx];
379 +
        } else if mem::eq(arg, "-ril") {
380 +
            try nextArg(args, &mut idx, &["`-ril` requires an output directory"]);
381 +
            set rilDirectory = args[idx];
364 382
        } else if mem::eq(arg, "-dump") {
365 383
            try nextArg(args, &mut idx, &["`-dump` requires a mode (eg. ast)"]);
366 384
            let mode = args[idx];
367 385
            if mem::eq(mode, "ast") {
368 386
                set dump = Dump::Ast;
415 433
    for i in 0..pkgCount {
416 434
        if i <> entryIdx and inputs[i].startupPath <> nil {
417 435
            throw error(&["`-start` is only supported on the entry package"]);
418 436
        }
419 437
    }
438 +
    if rilDirectory <> nil and (outputPath <> nil or dump <> Dump::None) {
439 +
        throw error(&["`-ril` requires a separate invocation from `-o` or `-dump`"]);
440 +
    }
420 441
    let graph = module::moduleGraph(&mut MODULE_ENTRIES[..], arena);
421 442
    let mut ctx = CompileContext {
422 443
        packages: undefined,
423 444
        inputs,
424 445
        packageCount: pkgCount,
425 446
        entryPkgIdx,
426 447
        graph,
427 448
        config: resolver::Config { buildTest },
428 449
        dump,
429 450
        outputPath,
451 +
        rilDirectory,
430 452
        debug: debugEnabled,
431 453
    };
432 454
    // Initialize and parse all packages.
433 455
    let mut sourceArena = alloc::new(&mut MODULE_SOURCES[..]);
434 456
    for i in 0..pkgCount {
1025 1047
        pkgLog(entryPkg, &["generating code", "(", path, ")", ".."]);
1026 1048
    }
1027 1049
    return rv64::finishProgram(&mut generator, low.data, storage, asmData, &mut RO_DATA_BUF[..], &mut RW_DATA_BUF[..]);
1028 1050
}
1029 1051
1052 +
/// Source exports selected for one binary RIL package.
1053 +
record PackageExports: Copy {
1054 +
    /// Number of initialized entries in the caller's export table.
1055 +
    count: u32,
1056 +
    /// Default function entry.
1057 +
    entry: ?*[u8],
1058 +
}
1059 +
1060 +
/// Collect exported definitions and the default entry from a package's source modules.
1061 +
unsafe fn packageExports(
1062 +
    ctx: &CompileContext,
1063 +
    pkg: &package::Package,
1064 +
    ilProgram: &il::Program,
1065 +
    exports: &mut [binary::Export],
1066 +
    arena: &mut alloc::Arena
1067 +
) -> PackageExports throws (Error) {
1068 +
    let mut count: u32 = 0;
1069 +
    let mut entryName: ?*[u8] = nil;
1070 +
    for i in 0..ctx.graph.entriesLen {
1071 +
        let modEntry = module::get(&ctx.graph, i as u16) else continue;
1072 +
        if modEntry.packageId <> pkg.id { continue; }
1073 +
        let root = modEntry.ast else continue;
1074 +
        let case ast::NodeValue::Block(block) = root.value else continue;
1075 +
        for node in block.statements {
1076 +
            let mut ident: ?*ast::Node = nil;
1077 +
            let mut attrs: ?ast::Attributes = nil;
1078 +
            let mut kind = binary::ExportKind::Function;
1079 +
            match node.value {
1080 +
                case ast::NodeValue::FnDecl(decl) => { set ident = decl.name; set attrs = decl.attrs; },
1081 +
                case ast::NodeValue::ConstDecl(decl) => {
1082 +
                    set ident = decl.ident; set attrs = decl.attrs; set kind = binary::ExportKind::Data;
1083 +
                },
1084 +
                case ast::NodeValue::StaticDecl(decl) => {
1085 +
                    set ident = decl.ident; set attrs = decl.attrs; set kind = binary::ExportKind::Data;
1086 +
                },
1087 +
                else => continue,
1088 +
            }
1089 +
            let attributes = attrs else continue;
1090 +
            let isDefault = ast::attributesContains(&attributes, ast::Attribute::Default)
1091 +
                and pkg.rootModuleId == modEntry.id;
1092 +
            if not isDefault and not ast::attributesContains(&attributes, ast::Attribute::Export) {
1093 +
                continue;
1094 +
            }
1095 +
            let nameNode = ident else continue;
1096 +
            let case ast::NodeValue::Ident(name) = nameNode.value else continue;
1097 +
            let qualified = il::formatQualifiedName(arena, module::moduleQualifiedPath(modEntry), name);
1098 +
            let mut present = false;
1099 +
            match kind {
1100 +
                case binary::ExportKind::Function => {
1101 +
                    for func in ilProgram.fns {
1102 +
                        if mem::eq(func.name, qualified) { set present = true; }
1103 +
                    }
1104 +
                },
1105 +
                case binary::ExportKind::Data => {
1106 +
                    for item in ilProgram.data {
1107 +
                        if mem::eq(item.name, qualified) { set present = true; }
1108 +
                    }
1109 +
                },
1110 +
            }
1111 +
            if not present { continue; }
1112 +
            if count == exports.len { throw error(&["too many package exports"]); }
1113 +
            set exports[count] = binary::Export { name: qualified, kind };
1114 +
            set count += 1;
1115 +
            if isDefault { set entryName = qualified; }
1116 +
        }
1117 +
    }
1118 +
    return PackageExports { count, entry: entryName };
1119 +
}
1120 +
1121 +
/// Emit one binary RIL file per package into an existing directory.
1122 +
unsafe fn emitPackages(ctx: *unsafe mut CompileContext, res: *unsafe mut resolver::Resolver, directory: *[u8]) throws (Error) {
1123 +
    for i in 0..ctx.packageCount {
1124 +
        if ctx.inputs[i].asmPathCount > 0 or ctx.inputs[i].startupPath <> nil {
1125 +
            throw error(&["binary RIL output requires Radiance source modules"]);
1126 +
        }
1127 +
    }
1128 +
    let unified = try lowerAllPackages(ctx, res);
1129 +
    let allocator = alloc::arenaAllocator(&mut res.arena);
1130 +
    for i in 0..ctx.packageCount {
1131 +
        let pkg = &ctx.packages[i];
1132 +
        let mut dataItems: *mut [il::Data] = &mut [];
1133 +
        let mut functions: *unsafe mut [*unsafe il::Fn] = &mut [];
1134 +
        for item in unified.data {
1135 +
            if ownsSymbol(pkg.name, item.name) { dataItems.append(item, allocator); }
1136 +
        }
1137 +
        for func in unified.fns {
1138 +
            if ownsSymbol(pkg.name, func.name) { functions.append(func, allocator); }
1139 +
        }
1140 +
        let local = il::Program { data: &dataItems[..], fns: functions };
1141 +
        let selected = try packageExports(ctx, pkg, &local, &mut PACKAGE_EXPORTS[..], &mut res.arena);
1142 +
        let mut dependencies: [*[u8]; MAX_PACKAGES] = undefined;
1143 +
        let mut names = collect::new(&mut PACKAGE_SYMBOLS[..], &mut dependencies[..]);
1144 +
        let image = try collect::package(&mut names, pkg.name, local, &PACKAGE_EXPORTS[..selected.count], selected.entry) catch {
1145 +
            throw error(&["cannot collect binary RIL package", pkg.name]);
1146 +
        };
1147 +
        let length = try program::encode(&mut FN_ARENA[..], &image) catch {
1148 +
            throw error(&["binary RIL output capacity exceeded", pkg.name]);
1149 +
        };
1150 +
        let mut path: [u8; MAX_PATH_LEN] = undefined;
1151 +
        let mut pos: u32 = 0;
1152 +
        for part in &[directory, "/", pkg.name, ".ril"] {
1153 +
            set pos += try mem::copy(&mut path[pos..MAX_PATH_LEN - 1], part) catch {
1154 +
                throw error(&["binary RIL output path is too long"]);
1155 +
            };
1156 +
        }
1157 +
        set path[pos] = 0;
1158 +
        if not unix::writeFile(&path[..pos], &FN_ARENA[..length]) {
1159 +
            throw error(&["cannot write binary RIL package", pkg.name]);
1160 +
        }
1161 +
    }
1162 +
}
1163 +
1164 +
/// Match a qualified definition to its package name.
1165 +
fn ownsSymbol(owner: *[u8], name: *[u8]) -> bool {
1166 +
    let suffix = mem::stripPrefix(owner, name) else return false;
1167 +
    return suffix.len > 2 and suffix[0] == ':' and suffix[1] == ':';
1168 +
}
1169 +
1030 1170
/// Lower, optionally dump, and optionally generate binary output.
1031 1171
unsafe fn compile(
1032 1172
    ctx: *unsafe mut CompileContext,
1033 1173
    res: *unsafe mut resolver::Resolver,
1034 1174
    fnArena: &mut alloc::Arena
1035 1175
) throws (Error) {
1036 1176
    let entryPkg = try getEntryPackage(ctx);
1177 +
    if let directory = ctx.rilDirectory {
1178 +
        try emitPackages(ctx, res, directory);
1179 +
        return;
1180 +
    }
1037 1181
    let mut out = sexpr::Output::Stdout;
1038 1182
1039 1183
    if ctx.dump == Dump::Il {
1040 1184
        // Lower all packages into a single unified IL program for dumping.
1041 1185
        let program = try lowerAllPackages(ctx, res);
lib/std/lang/il/binary.rad +1 -0
3 3
//! Optional fields use a byte: zero for absent, one for present.
4 4
5 5
export mod writer;
6 6
export mod reader;
7 7
export mod program;
8 +
export mod collect;
8 9
9 10
use std::lang::il;
10 11
@test export mod tests;
11 12
@test export mod decodeTests;
12 13
lib/std/lang/il/binary/collect.rad added +186 -0
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::Load { .. }, il::Instr::Sload { .. }, il::Instr::Unreachable,
133 +
             il::Instr::Ebreak, il::Instr::MemoryFence => {},
134 +
    }
135 +
}
136 +
137 +
/// Check that a definition belongs to the selected package.
138 +
unsafe fn definition(names: &mut Names, owner: *[u8], name: *[u8]) throws (binary::Error) {
139 +
    let rest = mem::stripPrefix(owner, name) else {
140 +
        throw binary::Error::Invalid;
141 +
    };
142 +
    if rest.len < 3 or rest[0] <> ':' or rest[1] <> ':' {
143 +
        throw binary::Error::Invalid;
144 +
    }
145 +
    try add(names, name);
146 +
}
147 +
148 +
/// Build package tables from local definitions and their qualified references.
149 +
/// Returned tables borrow `names`. Reset the collector before building another package.
150 +
export unsafe fn package(
151 +
    names: &mut Names,
152 +
    owner: *[u8],
153 +
    program: il::Program,
154 +
    exports: &[binary::Export],
155 +
    entry: ?*[u8]
156 +
) -> binary::Package throws (binary::Error) {
157 +
    try add(names, owner);
158 +
    for item in exports {
159 +
        try definition(names, owner, item.name);
160 +
    }
161 +
    if let name = entry {
162 +
        try definition(names, owner, name);
163 +
    }
164 +
    for item in program.data {
165 +
        try definition(names, owner, item.name);
166 +
        for data in item.values {
167 +
            match data.item {
168 +
                case il::DataItem::Sym(name) => try reference(names, owner, name),
169 +
                case il::DataItem::Fn(name) => try reference(names, owner, name),
170 +
                else => {},
171 +
            }
172 +
        }
173 +
    }
174 +
    for func in program.fns {
175 +
        try definition(names, owner, func.name);
176 +
        for block in func.blocks {
177 +
            for instr in block.instrs {
178 +
                try instruction(names, owner, instr);
179 +
            }
180 +
        }
181 +
    }
182 +
    return binary::Package {
183 +
        symbols: &names.symbols[..names.symbolCount], name: owner,
184 +
        dependencies: &names.dependencies[..names.dependencyCount], exports: exports as *unsafe [binary::Export], entry, program,
185 +
    };
186 +
}
lib/std/lang/il/binary/decodeTests.rad +41 -0
4 4
use std::lang::alloc;
5 5
use std::lang::il;
6 6
use std::lang::il::binary;
7 7
use std::lang::il::binary::reader;
8 8
use std::lang::il::binary::program;
9 +
use std::lang::il::binary::collect;
9 10
10 11
/// Decode arena backing storage. Tests reset it before each use.
11 12
static MEMORY: [u8; 2048] = [0; 2048];
12 13
13 14
/// Structural limits for small test programs.
302 303
        };
303 304
        try testing::expect(failed);
304 305
    }
305 306
}
306 307
308 +
/// Check symbol collection capacity and package ownership failures.
309 +
@test unsafe fn collectionBounds() throws (testing::TestError) {
310 +
    let mut symbols: [*[u8]; 8] = undefined;
311 +
    let mut dependencies: [*[u8]; 1] = undefined;
312 +
    let empty = il::Program { data: &[], fns: &[] };
313 +
    let mut names = collect::new(&mut symbols[..0], &mut dependencies[..]);
314 +
    let mut failed = false;
315 +
    try collect::package(&mut names, "p", empty, &[], nil) catch err {
316 +
        try testing::expect(err == binary::Error::Capacity);
317 +
        set failed = true;
318 +
    };
319 +
    try testing::expect(failed);
320 +
    let item = il::Data {
321 +
        name: "p::data", size: 8, alignment: 8, readOnly: false, isZeroInit: false,
322 +
        values: &[il::DataValue { item: il::DataItem::Fn("dep::fn"), count: 1 }],
323 +
    };
324 +
    let local = il::Program { data: retainData(&[item]), fns: &[] };
325 +
    set names = collect::new(&mut symbols[..], &mut dependencies[..0]);
326 +
    set failed = false;
327 +
    try collect::package(&mut names, "p", local, &[], nil) catch err {
328 +
        try testing::expect(err == binary::Error::Capacity);
329 +
        set failed = true;
330 +
    };
331 +
    try testing::expect(failed);
332 +
    set names = collect::new(&mut symbols[..], &mut dependencies[..]);
333 +
    set failed = false;
334 +
    try collect::package(&mut names, "other", local, &[], nil) catch err {
335 +
        try testing::expect(err == binary::Error::Invalid);
336 +
        set failed = true;
337 +
    };
338 +
    try testing::expect(failed);
339 +
    set names = collect::new(&mut symbols[..], &mut dependencies[..]);
340 +
    let package = try collect::package(&mut names, "p", local, &[], nil) catch {
341 +
        throw testing::TestError::Failed;
342 +
    };
343 +
    try testing::expect(package.dependencies.len == 1);
344 +
    try testing::expectBytesEq(package.dependencies[0], "dep");
345 +
    try testing::expect(package.symbols.len == 4);
346 +
}
347 +
307 348
/// Copy data fixtures into stable storage for the package descriptor.
308 349
unsafe fn retainData(items: &[il::Data]) -> *[il::Data] {
309 350
    unsafe static DATA: [il::Data; 2] = undefined;
310 351
    assert items.len <= DATA.len;
311 352
    for item, i in items { set DATA[i] = item; }
std.lib +1 -0
33 33
lib/std/lang/il/printer.rad
34 34
lib/std/lang/il/binary.rad
35 35
lib/std/lang/il/binary/writer.rad
36 36
lib/std/lang/il/binary/reader.rad
37 37
lib/std/lang/il/binary/program.rad
38 +
lib/std/lang/il/binary/collect.rad
38 39
lib/std/lang/resolver.rad
39 40
lib/std/lang/resolver/printer.rad
40 41
lib/std/lang/lower.rad
41 42
lib/std/lang/module.rad
42 43
lib/std/lang/module/printer.rad