compiler/
kernel/
lib/
examples/
std/
arch/
char/
collections/
lang/
alloc/
ast/
gen/
il/
binary/
images/
graph.rad
15.5 KiB
layout.rad
7.4 KiB
native.rad
18.5 KiB
binary.rad
13.9 KiB
images.rad
10.6 KiB
printer.rad
14.7 KiB
module/
parser/
resolver/
scanner/
alloc.rad
4.3 KiB
ast.rad
23.3 KiB
gen.rad
513 B
il.rad
16.1 KiB
lower.rad
272.5 KiB
module.rad
13.2 KiB
package.rad
1.3 KiB
parser.rad
77.8 KiB
resolver.rad
308.6 KiB
scanner.rad
17.5 KiB
sexpr.rad
6.3 KiB
strings.rad
2.2 KiB
types.rad
286 B
sys/
arch.rad
68 B
char.rad
855 B
collections.rad
39 B
fmt.rad
8.1 KiB
intrinsics.rad
467 B
io.rad
1.3 KiB
lang.rad
276 B
mem.rad
2.2 KiB
sys.rad
179 B
testing.rad
2.4 KiB
tests.rad
15.2 KiB
vec.rad
3.2 KiB
std.rad
281 B
scripts/
seed/
sublime/
test/
vim/
.gitignore
351 B
.gitsigners
112 B
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
3.8 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.4 KiB
std.lib.test
380 B
lib/std/lang/il/images/layout.rad
raw
| 1 | //! Position-independent native layout and persistent pointer initialization. |
| 2 | |
| 3 | use std::mem; |
| 4 | use std::collections::dict; |
| 5 | use std::lang::alloc; |
| 6 | use std::lang::il; |
| 7 | use std::lang::gen::data; |
| 8 | use std::arch::rv64; |
| 9 | use std::arch::rv64::emit; |
| 10 | use std::arch::rv64::encode; |
| 11 | use super::graph; |
| 12 | |
| 13 | /// Native bytes and offsets, with data addresses relative to the allocation. |
| 14 | export record Layout: Copy { |
| 15 | /// Fully initialized read-only bytes with allocation-relative pointers. |
| 16 | bytes: *[u8], |
| 17 | /// Allocation-relative immutable symbol addresses. |
| 18 | symbols: data::DataSymMap, |
| 19 | /// Byte offset of the first native instruction. |
| 20 | codeOffset: u32, |
| 21 | /// Complete allocation size including native instructions. |
| 22 | size: u32, |
| 23 | /// Required power-of-two allocation alignment. |
| 24 | alignment: u32, |
| 25 | } |
| 26 | |
| 27 | /// Resolve a function before invoking backend relocation routines. |
| 28 | export fn functionOffset(e: *emit::Emitter, name: *[u8]) -> u32 throws (il::binary::Error) { |
| 29 | let offset = dict::get(&e.labels.funcs, name) else { |
| 30 | throw il::binary::error(0, "unresolved native image function"); |
| 31 | }; |
| 32 | if offset < 0 or offset as u32 % 4 <> 0 or offset as u32 / 4 >= e.codeLen { |
| 33 | throw il::binary::error(0, "native image function offset exceeds code"); |
| 34 | } |
| 35 | return offset as u32; |
| 36 | } |
| 37 | |
| 38 | /// Require every reserved relocation instruction to belong to emitted code. |
| 39 | fn slots(e: *emit::Emitter, index: u32, count: u32) throws (il::binary::Error) { |
| 40 | if index > e.codeLen or count > e.codeLen - index { |
| 41 | throw il::binary::error(0, "native image relocation exceeds code"); |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | /// Check all references before the shared emitter's infallible final patching. |
| 46 | fn references(e: *emit::Emitter, items: *[il::Data], symbols: *data::DataSymMap) throws (il::binary::Error) { |
| 47 | if e.pendingBranches.len <> 0 { throw il::binary::error(0, "unresolved native image block branch"); } |
| 48 | for pending in e.pendingCalls { |
| 49 | try slots(e, pending.index, 2); |
| 50 | let _ = try functionOffset(e, pending.target); |
| 51 | } |
| 52 | for pending in e.pendingJumps { |
| 53 | try slots(e, pending.index, 1); |
| 54 | let offset = try functionOffset(e, pending.target); |
| 55 | if not encode::isJumpImm(offset as i32 - pending.index as i32 * rv64::INSTR_SIZE) { |
| 56 | throw il::binary::error(0, "native image jump exceeds reach"); |
| 57 | } |
| 58 | } |
| 59 | for pending in e.pendingAddrLoads { |
| 60 | try slots(e, pending.index, 2); |
| 61 | if pending.isData { |
| 62 | if data::lookupAddr(symbols, pending.target) == nil { |
| 63 | throw il::binary::error(0, "unresolved native image data address"); |
| 64 | } |
| 65 | } else { let _ = try functionOffset(e, pending.target); } |
| 66 | } |
| 67 | for item in items { |
| 68 | for value in item.values { |
| 69 | if value.count == 0 { continue; } |
| 70 | match value.item { |
| 71 | case il::DataItem::Sym(name) => { |
| 72 | if data::lookupAddr(symbols, name) == nil { |
| 73 | throw il::binary::error(0, "unresolved native image data initializer"); |
| 74 | } |
| 75 | } |
| 76 | case il::DataItem::Fn(name) => { let _ = try functionOffset(e, name); } |
| 77 | else => {}, |
| 78 | } |
| 79 | } |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | /// Lay out flattened immutable data using the shared data placement and emitter. |
| 84 | /// Code and data address instructions use allocation-local PC-relative offsets. |
| 85 | export fn prepare(generator: *mut rv64::Generator, items: *[il::Data], arena: *mut alloc::Arena, |
| 86 | scratch: *mut alloc::Arena) -> Layout throws (il::binary::Error) { |
| 87 | let mut alignment: u32 = 8; |
| 88 | let mut extent: u64 = 0; |
| 89 | for item in items { |
| 90 | let a = item.alignment as u64; |
| 91 | if not item.readOnly or item.isZeroInit or a == 0 or a & (a - 1) <> 0 or a > graph::MAX_DATA as u64 { |
| 92 | throw il::binary::error(0, "invalid flattened native image data"); |
| 93 | } |
| 94 | if item.alignment > alignment { set alignment = item.alignment; } |
| 95 | set extent = ((extent + a - 1) & ~(a - 1)) + item.size as u64; |
| 96 | if extent > graph::MAX_DATA as u64 { throw il::binary::error(0, "native image data exceeds capacity"); } |
| 97 | let mut size: u64 = 0; |
| 98 | for value in item.values { |
| 99 | set size += graph::width(value.item) as u64 * value.count as u64; |
| 100 | if size > item.size as u64 { throw il::binary::error(0, "native image initializer exceeds data"); } |
| 101 | } |
| 102 | if size <> item.size as u64 { throw il::binary::error(0, "native image initializer size mismatch"); } |
| 103 | } |
| 104 | let syms = try graph::storage(scratch, @sizeOf(data::DataSym), @alignOf(data::DataSym), items.len) as *mut [data::DataSym]; |
| 105 | let mut count: u32 = 0; |
| 106 | let dataSize = data::layoutSection(items, syms, &mut count, 0, true); |
| 107 | let mut map = try il::binary::dictionary(scratch, count, 0); |
| 108 | for symbol in syms { |
| 109 | if dict::get(&map, symbol.name) <> nil { throw il::binary::error(0, "duplicate native image data symbol"); } |
| 110 | dict::insert(&mut map, symbol.name, symbol.addr as i32); |
| 111 | } |
| 112 | let symbols = data::DataSymMap { dict: map, syms }; |
| 113 | let e = &mut generator.e; |
| 114 | let codeOffset = mem::alignUp(dataSize, 8); |
| 115 | let total = codeOffset as u64 + e.codeLen as u64 * rv64::INSTR_SIZE as u64; |
| 116 | // Keep every allocation-local AUIPC displacement strictly inside signed reach. |
| 117 | if total > 0x7FFFF000 { throw il::binary::error(0, "native image exceeds relative address reach"); } |
| 118 | try references(e, items, &symbols); |
| 119 | let bytes = try graph::storage(arena, 1, 1, dataSize) as *mut [u8]; |
| 120 | for i in 0..bytes.len { set bytes[i] = 0; } |
| 121 | emit::patchCalls(e); |
| 122 | emit::patchJumps(e); |
| 123 | for pending in e.pendingAddrLoads { |
| 124 | let mut target: u32 = 0; |
| 125 | if pending.isData { |
| 126 | let address = data::lookupAddr(&symbols, pending.target) else { |
| 127 | throw il::binary::error(0, "unresolved native image data address"); |
| 128 | }; |
| 129 | set target = address; |
| 130 | } else { set target = codeOffset + (try functionOffset(e, pending.target)); } |
| 131 | let pc = codeOffset + pending.index * rv64::INSTR_SIZE as u32; |
| 132 | let split = emit::splitImm(target as i32 - pc as i32); |
| 133 | emit::patch(e, pending.index, encode::auipc(pending.rd, split.hi)); |
| 134 | emit::patch(e, pending.index + 1, encode::addi(pending.rd, pending.rd, split.lo)); |
| 135 | } |
| 136 | data::emitSection(items, &symbols, &e.labels, codeOffset, bytes, true); |
| 137 | return Layout { bytes, symbols, codeOffset, size: total as u32, alignment }; |
| 138 | } |
| 139 | |
| 140 | /// Convert allocation-relative initializer pointers to actual native addresses. |
| 141 | /// Scalar private offsets remain unchanged for the per-instance relocation helper. |
| 142 | export fn rebase(items: *[il::Data], destination: *mut [u8], base: u64) { |
| 143 | let mut offset: u32 = 0; |
| 144 | for item in items { |
| 145 | set offset = mem::alignUp(offset, item.alignment); |
| 146 | for value in item.values { |
| 147 | match value.item { |
| 148 | case il::DataItem::Sym(_), il::DataItem::Fn(_) => { |
| 149 | for _ in 0..value.count { |
| 150 | let mut relative: u64 = 0; |
| 151 | for byte in 0..8 { set relative |= (destination[offset + byte] as u64) << (byte as u64 * 8); } |
| 152 | let address = base + relative; |
| 153 | for byte in 0..8 { set destination[offset + byte] = (address >> (byte as u64 * 8)) as u8; } |
| 154 | set offset += 8; |
| 155 | } |
| 156 | } |
| 157 | else => set offset += graph::width(value.item) * value.count, |
| 158 | } |
| 159 | } |
| 160 | } |
| 161 | } |