rv64: Generate shared package code

1bb6778bd897513d35d648f6e7aad34bafb51dcacad4470897eb2dce337149d9
Assisted-by: Codex:gpt-6
Alexis Sellier committed ago 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 +
}