rv64: Generate shared package code
1bb6778bd897513d35d648f6e7aad34bafb51dcacad4470897eb2dce337149d9
Assisted-by: Codex:gpt-6
1 parent
b7fbdab5
test/shared/app.rad
added
+16 -0
| 1 | + | //! Shared entry with callbacks, initialized data pointers, and private counters. |
|
| 2 | + | ||
| 3 | + | use support; |
|
| 4 | + | ||
| 5 | + | /// Callback table contains a linked dependency function address. |
|
| 6 | + | static CALLBACKS: [fn() -> i32; 1] = [support::read]; |
|
| 7 | + | /// The slice contains a pointer into this domain's string storage. |
|
| 8 | + | static TEXT: *[u8] = "xy"; |
|
| 9 | + | /// Number of calls in the current domain. |
|
| 10 | + | static calls: i32 = 0; |
|
| 11 | + | ||
| 12 | + | /// Read dependency state through calls and data, then advance private state. |
|
| 13 | + | @default export fn main() -> i32 { |
|
| 14 | + | set calls += 1; |
|
| 15 | + | return support::read() + CALLBACKS[0]() + support::value + TEXT[0] as i32 + calls; |
|
| 16 | + | } |
test/shared/build.rad
added
+159 -0
| 1 | + | //! Build a machine fixture from separately decoded and linked packages. |
|
| 2 | + | ||
| 3 | + | use std::sys; |
|
| 4 | + | use std::sys::unix; |
|
| 5 | + | use std::mem; |
|
| 6 | + | use std::lang::alloc; |
|
| 7 | + | use std::lang::il::binary; |
|
| 8 | + | use std::lang::il::binary::program; |
|
| 9 | + | use std::lang::gen::data; |
|
| 10 | + | use std::arch::rv64; |
|
| 11 | + | use std::arch::rv64::shared; |
|
| 12 | + | use std::arch::rv64::emit; |
|
| 13 | + | use std::arch::rv64::encode; |
|
| 14 | + | use std::arch::rv64::image; |
|
| 15 | + | ||
| 16 | + | /// Separate code arenas preserve the boot catalog's package records. |
|
| 17 | + | unsafe static CODE: [[u8; 16777216]; 3] = undefined; |
|
| 18 | + | /// Reusable function workspace. |
|
| 19 | + | static SCRATCH: [u8; 16777216] = [0; 16777216]; |
|
| 20 | + | /// Decoder arena shared by the two input packages. |
|
| 21 | + | static DECODE: [u8; 1048576] = [0; 1048576]; |
|
| 22 | + | /// Input file buffer. |
|
| 23 | + | static INPUT: [u8; 65536] = [0; 65536]; |
|
| 24 | + | /// Persistent private templates. |
|
| 25 | + | unsafe static TEMPLATES: [[u8; 16384]; 2] = undefined; |
|
| 26 | + | /// Local symbol workspaces. |
|
| 27 | + | unsafe static SYMBOLS: [[shared::Symbol; 128]; 2] = undefined; |
|
| 28 | + | /// Package exports. |
|
| 29 | + | unsafe static EXPORTS: [[shared::Symbol; 128]; 2] = undefined; |
|
| 30 | + | /// Private pointer fixups. |
|
| 31 | + | unsafe static RELOCS: [[shared::Relocation; 128]; 2] = undefined; |
|
| 32 | + | /// Data layout workspaces. |
|
| 33 | + | unsafe static DATA: [[data::DataSym; 128]; 2] = undefined; |
|
| 34 | + | /// Native instruction image, with fixed package placements. |
|
| 35 | + | static TEXT: [u32; 4096] = [0; 4096]; |
|
| 36 | + | /// Two private graphs followed by their package-state tables. |
|
| 37 | + | static STATE: [u8; 69632] = [0; 69632]; |
|
| 38 | + | ||
| 39 | + | /// Decode trusted package IL into persistent storage. |
|
| 40 | + | unsafe fn load(path: *[u8], arena: &mut alloc::Arena) -> binary::Package { |
|
| 41 | + | let length = unix::readFile(path, &mut INPUT[..]) else panic "missing package"; |
|
| 42 | + | return try! program::decode(&INPUT[..length], arena, binary::Limits { registers: 8192, blocks: 4096 }); |
|
| 43 | + | } |
|
| 44 | + | ||
| 45 | + | /// Compile through the same entry used for boot catalogs and runtime loading. |
|
| 46 | + | unsafe fn compile(input: &binary::Package, index: u32, imports: &[shared::Symbol]) -> shared::Package throws (shared::Error) { |
|
| 47 | + | let mut arena = alloc::new(&mut CODE[index][..]); |
|
| 48 | + | let mut scratch = alloc::new(&mut SCRATCH[..]); |
|
| 49 | + | return try shared::compile(input, index, 0x80011000 + index as u64 * 4096, imports, |
|
| 50 | + | shared::Storage { |
|
| 51 | + | data: &mut DATA[index][..], symbols: &mut SYMBOLS[index][..], exports: &mut EXPORTS[index][..], |
|
| 52 | + | template: &mut TEMPLATES[index][..], relocations: &mut RELOCS[index][..], |
|
| 53 | + | }, &mut arena, &mut scratch); |
|
| 54 | + | } |
|
| 55 | + | ||
| 56 | + | /// Append a call result check and return its branch patch location. |
|
| 57 | + | fn check(e: &mut emit::Emitter, entry: u64, expected: i64) -> u32 { |
|
| 58 | + | let delta = entry - (0x80010000 + e.codeLen as u64 * 4); |
|
| 59 | + | emit::emit(e, encode::jal(rv64::RA, delta as i32)); |
|
| 60 | + | emit::loadImm(e, rv64::T0, expected); |
|
| 61 | + | let index = e.codeLen; |
|
| 62 | + | emit::emit(e, encode::nop()); |
|
| 63 | + | return index; |
|
| 64 | + | } |
|
| 65 | + | ||
| 66 | + | /// Emit an explicit platform completion status. |
|
| 67 | + | fn finish(e: &mut emit::Emitter, status: i64) { |
|
| 68 | + | emit::loadImm(e, rv64::T0, 0x10001000); |
|
| 69 | + | emit::loadImm(e, rv64::T1, status); |
|
| 70 | + | emit::emit(e, encode::sw(rv64::T1, rv64::T0, 0)); |
|
| 71 | + | emit::emit(e, encode::ebreak()); |
|
| 72 | + | } |
|
| 73 | + | ||
| 74 | + | /// Verify catalog linking, then execute one code copy against two private graphs. |
|
| 75 | + | @default unsafe fn main(env: *sys::Env) -> i32 { |
|
| 76 | + | assert env.args.len == 4; |
|
| 77 | + | let mut decoder = alloc::new(&mut DECODE[..]); |
|
| 78 | + | let supportInput = load(env.args[1], &mut decoder); |
|
| 79 | + | let appInput = load(env.args[2], &mut decoder); |
|
| 80 | + | let support = try! compile(&supportInput, 0, &[]); |
|
| 81 | + | let mut far: [shared::Symbol; 128] = undefined; |
|
| 82 | + | for symbol, i in support.exports { |
|
| 83 | + | set far[i] = symbol; |
|
| 84 | + | match symbol.target { |
|
| 85 | + | case shared::Target::Function(_) => { |
|
| 86 | + | set far[i].target = shared::Target::Function(0x180000000); |
|
| 87 | + | }, |
|
| 88 | + | else => { |
|
| 89 | + | }, |
|
| 90 | + | } |
|
| 91 | + | } |
|
| 92 | + | let mut rejected = false; |
|
| 93 | + | try compile(&appInput, 1, &far[..support.exports.len]) catch err { |
|
| 94 | + | assert err == shared::Error::Range; |
|
| 95 | + | set rejected = true; |
|
| 96 | + | }; |
|
| 97 | + | assert rejected; |
|
| 98 | + | let app = try! compile(&appInput, 1, support.exports); |
|
| 99 | + | let catalog = [support, app]; |
|
| 100 | + | assert app.relocations.len > 0; |
|
| 101 | + | let entry = app.entry else panic "missing entry"; |
|
| 102 | + | for i in 0..STATE.len { |
|
| 103 | + | set STATE[i] = 0; |
|
| 104 | + | } |
|
| 105 | + | for domain in 0..2 { |
|
| 106 | + | let mut bases: [u64; 256] = [0; 256]; |
|
| 107 | + | for package, i in &catalog[..] { |
|
| 108 | + | let offset = domain * 32768 + i * 16384; |
|
| 109 | + | set bases[i] = 0x80020000 + offset as u64; |
|
| 110 | + | } |
|
| 111 | + | for package, i in &catalog[..] { |
|
| 112 | + | let offset = domain * 32768 + i * 16384; |
|
| 113 | + | try! shared::instantiate(&package, &bases[..], &mut STATE[offset..offset + 16384]); |
|
| 114 | + | } |
|
| 115 | + | let table = @sliceOf(&bases[0] as *unsafe u8, @sizeOf([u64; 256])); |
|
| 116 | + | let tableOffset: u32 = 65536 + domain * 2048; |
|
| 117 | + | try! mem::copy(&mut STATE[tableOffset..], table); |
|
| 118 | + | } |
|
| 119 | + | let mut arena = alloc::new(&mut CODE[2][..]); |
|
| 120 | + | let mut e = try! emit::emitter(&mut arena, false); |
|
| 121 | + | emit::loadImm(&mut e, rv64::GP, 0x80030000); |
|
| 122 | + | let first = check(&mut e, entry, 142); |
|
| 123 | + | let second = check(&mut e, entry, 143); |
|
| 124 | + | emit::loadImm(&mut e, rv64::GP, 0x80030800); |
|
| 125 | + | let other = check(&mut e, entry, 142); |
|
| 126 | + | emit::loadImm(&mut e, rv64::GP, 0x80030000); |
|
| 127 | + | let resumed = check(&mut e, entry, 144); |
|
| 128 | + | finish(&mut e, 0x5555); |
|
| 129 | + | let failure = e.codeLen; |
|
| 130 | + | finish(&mut e, 0x13333); |
|
| 131 | + | for index in &[first, second, other, resumed] { |
|
| 132 | + | emit::patch(&mut e, index, encode::bne(rv64::A0, rv64::T0, (failure - index) as i32 * 4)); |
|
| 133 | + | } |
|
| 134 | + | for i in 0..TEXT.len { |
|
| 135 | + | set TEXT[i] = encode::nop(); |
|
| 136 | + | } |
|
| 137 | + | assert e.codeLen <= 1024; |
|
| 138 | + | for word, i in emit::getCode(&e) { |
|
| 139 | + | set TEXT[i] = word; |
|
| 140 | + | } |
|
| 141 | + | for package, i in &catalog[..] { |
|
| 142 | + | assert package.code.len <= 1024; |
|
| 143 | + | for word, j in package.code { |
|
| 144 | + | set TEXT[1024 + i * 1024 + j] = word; |
|
| 145 | + | } |
|
| 146 | + | } |
|
| 147 | + | let header = try! image::header(image::Layout { |
|
| 148 | + | entry: 0x80010000, |
|
| 149 | + | code: image::Segment { address: 0x80010000, initialized: 16384, memory: 16384 }, |
|
| 150 | + | roData: image::Segment { address: 0, initialized: 0, memory: 0 }, |
|
| 151 | + | rwData: image::Segment { address: 0x80020000, initialized: STATE.len, memory: STATE.len }, |
|
| 152 | + | }); |
|
| 153 | + | let fd = unix::openOpts(env.args[3], unix::OpenFlags(*unix::O_WRONLY | *unix::O_CREAT | *unix::O_TRUNC), 420); |
|
| 154 | + | assert fd >= 0; |
|
| 155 | + | let written = unix::writeAll(fd, &header[..]) and unix::writeAll(fd, @sliceOf(&TEXT[0] as *u8, 16384)) and unix::writeAll(fd, &STATE[..]); |
|
| 156 | + | let closed = unix::close(fd) == 0; |
|
| 157 | + | assert written and closed; |
|
| 158 | + | return 0; |
|
| 159 | + | } |
test/shared/run
added
+13 -0
| 1 | + | #!/bin/sh |
|
| 2 | + | # Compile two packages once and execute them with two private state graphs. |
|
| 3 | + | set -eu |
|
| 4 | + | emulator=${RAD_EMULATOR:-emulator} |
|
| 5 | + | work=$(mktemp -d) |
|
| 6 | + | trap 'rm -rf "$work"' EXIT HUP INT TERM |
|
| 7 | + | "$emulator" -memory-size=385024 -data-size=348160 -stack-size=512 \ |
|
| 8 | + | -run bin/radiance.rv64.dev \ |
|
| 9 | + | -pkg support -mod test/shared/support.rad \ |
|
| 10 | + | -pkg app -mod test/shared/app.rad -entry app -ril "$work" |
|
| 11 | + | "$emulator" -run bin/shared.build.rv64 -- "$work/support.ril" "$work/app.ril" "$work/native.rv64" |
|
| 12 | + | "$emulator" -machine -run "$work/native.rv64" |
|
| 13 | + | printf 'shared packages: calls, callbacks, private pointers, and independent state passed\n' |
test/shared/support.rad
added
+15 -0
| 1 | + | //! Shared dependency with private writable state. |
|
| 2 | + | ||
| 3 | + | /// Value read by dependent packages. |
|
| 4 | + | export static value: i32 = 7; |
|
| 5 | + | ||
| 6 | + | /// Large private allocation before the final zero-initialized scalar. |
|
| 7 | + | static padding: [u8; 8192] = [0; 8192]; |
|
| 8 | + | /// Zero-initialized value at a large package offset. |
|
| 9 | + | static tail: i32 = 0; |
|
| 10 | + | ||
| 11 | + | /// Read this domain's value. |
|
| 12 | + | export fn read() -> i32 { |
|
| 13 | + | set padding[8191] = 0; |
|
| 14 | + | return value + tail; |
|
| 15 | + | } |