compiler: Emit separate binary RIL packages

8e1da2cd296ca5c9b1938d14a8e9595d4f41bccbb734a932b2df54bf67aa2fe2
Assisted-by: Codex:gpt-6
Alexis Sellier committed ago 1 parent 4da4070b
test/packages/app.rad added +4 -0
1 +
//! Application package fixture.
2 +
use support;
3 +
/// Return the shared package's state.
4 +
@default fn main() -> u64 { return support::read() + support::value; }
test/packages/check.rad added +75 -0
1 +
//! Verify compiler-emitted binary RIL package boundaries.
2 +
3 +
use std::sys;
4 +
use std::sys::unix;
5 +
use std::mem;
6 +
use std::lang::alloc;
7 +
use std::lang::il;
8 +
use std::lang::il::binary;
9 +
use std::lang::il::binary::program;
10 +
11 +
/// Maximum fixture image size.
12 +
constant IMAGE_SIZE: u32 = 4096;
13 +
/// Arena capacity for both decoded fixture packages.
14 +
constant ARENA_SIZE: u32 = 32768;
15 +
/// Input image storage.
16 +
static IMAGE: [u8; IMAGE_SIZE] = [0; IMAGE_SIZE];
17 +
/// Decoded package storage.
18 +
static MEMORY: [u8; ARENA_SIZE] = [0; ARENA_SIZE];
19 +
20 +
/// Decode one compiler output file.
21 +
unsafe fn load(path: *[u8], arena: &mut alloc::Arena) -> binary::Package {
22 +
    let length = unix::readFile(path, &mut IMAGE[..]) else panic "load: cannot read fixture";
23 +
    return try! program::decode(&IMAGE[..length], arena, binary::Limits { registers: 8192, blocks: 4096 });
24 +
}
25 +
26 +
/// Check identity, ownership, linkage, and source exports for two packages.
27 +
@default unsafe fn main(env: *sys::Env) -> i32 {
28 +
    assert env.args.len == 3, "main: expected two package paths";
29 +
    let mut arena = alloc::new(&mut MEMORY[..]);
30 +
    let support = load(env.args[1], &mut arena);
31 +
    let app = load(env.args[2], &mut arena);
32 +
    assert mem::eq(support.name, "support"), "main: mem::eq(support.name, \"support\")";
33 +
    assert mem::eq(app.name, "app"), "main: mem::eq(app.name, \"app\")";
34 +
    assert support.dependencies.len == 0, "main: support.dependencies.len == 0";
35 +
    assert app.dependencies.len == 1, "main: app.dependencies.len == 1";
36 +
    assert mem::eq(app.dependencies[0], "support"), "main: mem::eq(app.dependencies[0], \"support\")";
37 +
    assert support.program.fns.len == 1, "main: support.program.fns.len == 1";
38 +
    assert support.program.data.len == 1, "main: support.program.data.len == 1";
39 +
    assert mem::eq(support.program.fns[0].name, "support::read"), "main: mem::eq(support.program.fns[0].name, \"support::read\")";
40 +
    assert mem::eq(support.program.data[0].name, "support::value"), "main: mem::eq(support.program.data[0].name, \"support::value\")";
41 +
    assert app.program.fns.len == 1, "main: app.program.fns.len == 1";
42 +
    assert app.program.data.len == 0, "main: app.program.data.len == 0";
43 +
    assert mem::eq(app.program.fns[0].name, "app::main"), "main: mem::eq(app.program.fns[0].name, \"app::main\")";
44 +
    assert support.exports.len == 2, "main: support.exports.len == 2";
45 +
    assert app.exports.len == 1, "main: app.exports.len == 1";
46 +
    assert app.exports[0].kind == binary::ExportKind::Function, "main: app.exports[0].kind == binary::ExportKind::Function";
47 +
    let entry = app.entry else panic "main: missing package entry";
48 +
    assert mem::eq(entry, "app::main"), "main: mem::eq(entry, \"app::main\")";
49 +
    let mut callFound = false;
50 +
    let mut dataFound = false;
51 +
    for block in app.program.fns[0].blocks {
52 +
        for instr in block.instrs {
53 +
            match instr {
54 +
                case il::Instr::Call { func, .. } => {
55 +
                    if let case il::Val::FnAddr(name) = func {
56 +
                        if mem::eq(name, "support::read") {
57 +
                            set callFound = true;
58 +
                        }
59 +
                    }
60 +
                },
61 +
                case il::Instr::Copy { val, .. } => {
62 +
                    if let case il::Val::DataSym(name) = val {
63 +
                        if mem::eq(name, "support::value") {
64 +
                            set dataFound = true;
65 +
                        }
66 +
                    }
67 +
                },
68 +
                else => {
69 +
                },
70 +
            }
71 +
        }
72 +
    }
73 +
    assert callFound and dataFound, "main: callFound and dataFound";
74 +
    return 0;
75 +
}
test/packages/run added +24 -0
1 +
#!/bin/sh
2 +
# Check separate RIL package output and reproducible bytes.
3 +
set -eu
4 +
emulator=${RAD_EMULATOR:-emulator}
5 +
work=$(mktemp -d)
6 +
trap 'rm -rf "$work"' EXIT HUP INT TERM
7 +
mkdir "$work/first" "$work/second"
8 +
for output in first second; do
9 +
    "$emulator" -memory-size=385024 -data-size=348160 -stack-size=512 \
10 +
        -run bin/radiance.rv64.dev \
11 +
        -pkg support -mod test/packages/support.rad \
12 +
        -pkg app -mod test/packages/app.rad -entry app -ril "$work/$output"
13 +
done
14 +
cmp "$work/first/support.ril" "$work/second/support.ril"
15 +
cmp "$work/first/app.ril" "$work/second/app.ril"
16 +
"$emulator" -run bin/packages.check.rv64 -- "$work/first/support.ril" "$work/first/app.ril"
17 +
"$emulator" -memory-size=385024 -data-size=348160 -stack-size=512 \
18 +
    -run bin/radiance.rv64.dev \
19 +
    -pkg support -mod test/packages/support.rad \
20 +
    -pkg app -mod test/packages/app.rad -entry app -o "$work/hosted.rv64"
21 +
status=0
22 +
"$emulator" -run "$work/hosted.rv64" || status=$?
23 +
[ "$status" -eq 14 ]
24 +
printf 'package output: ownership, linkage, exports, determinism, and hosted execution passed\n'
test/packages/support.rad added +5 -0
1 +
//! Shared package fixture.
2 +
/// Shared mutable data.
3 +
export static value: u64 = 7;
4 +
/// Read shared mutable data.
5 +
export fn read() -> u64 { return value; }