compiler: Emit separate binary RIL packages
8e1da2cd296ca5c9b1938d14a8e9595d4f41bccbb734a932b2df54bf67aa2fe2
Assisted-by: Codex:gpt-6
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; } |