compiler/
lib/
examples/
std/
arch/
char/
collections/
lang/
alloc/
ast/
gen/
bitset/
regalloc/
bitset.rad
6.1 KiB
data.rad
8.6 KiB
labels.rad
2.4 KiB
regalloc.rad
2.3 KiB
types.rad
594 B
il/
module/
parser/
resolver/
scanner/
alloc.rad
5.3 KiB
ast.rad
23.6 KiB
gen.rad
513 B
il.rad
15.5 KiB
lower.rad
277.2 KiB
module.rad
13.5 KiB
package.rad
1.3 KiB
parser.rad
79.6 KiB
resolver.rad
332.7 KiB
scanner.rad
17.6 KiB
sexpr.rad
6.4 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.3 KiB
intrinsics.rad
467 B
io.rad
1.7 KiB
lang.rad
276 B
mem.rad
2.3 KiB
sys.rad
179 B
testing.rad
2.4 KiB
tests.rad
15.7 KiB
vec.rad
3.2 KiB
std.rad
281 B
scripts/
seed/
sublime/
test/
vim/
.gitignore
336 B
.gitsigners
112 B
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
4.1 KiB
README
2.5 KiB
STYLE
2.6 KiB
std.lib
1.2 KiB
std.lib.test
347 B
lib/std/lang/gen/data.rad
raw
| 1 | //! Data section layout and emission. |
| 2 | //! |
| 3 | //! Target-independent routines for laying out data symbols and |
| 4 | //! serializing initialized data into binary sections. |
| 5 | |
| 6 | use std::mem; |
| 7 | use std::collections::dict; |
| 8 | use std::lang::il; |
| 9 | use std::lang::gen::labels; |
| 10 | |
| 11 | /// Maximum number of data symbols. |
| 12 | export constant MAX_DATA_SYMS: u32 = 8192; |
| 13 | |
| 14 | /// Size of the data symbol hash table. Must be a power of two |
| 15 | /// and at least twice the size of [`MAX_DATA_SYMS`]. |
| 16 | export constant DATA_SYM_TABLE_SIZE: u32 = MAX_DATA_SYMS * 2; |
| 17 | |
| 18 | /// Data section layout or emission failure. |
| 19 | export union Error: Copy { |
| 20 | /// A required symbol is missing or duplicated. |
| 21 | Symbol, |
| 22 | /// A symbol table or output buffer is full. |
| 23 | Capacity, |
| 24 | /// A size or address exceeds the supported range. |
| 25 | Overflow, |
| 26 | /// Alignment is zero or is not a power of two. |
| 27 | Alignment, |
| 28 | } |
| 29 | |
| 30 | /// Data symbol entry mapping name to address. |
| 31 | export record DataSym: Copy { |
| 32 | /// Symbol name. |
| 33 | name: *[u8], |
| 34 | /// Absolute address, including data base address. |
| 35 | addr: u32, |
| 36 | } |
| 37 | |
| 38 | /// Hash-indexed data symbol map. |
| 39 | export record DataSymMap { |
| 40 | /// Underlying hash table. |
| 41 | dict: dict::Dict, |
| 42 | /// Fallback linear array for edge cases. |
| 43 | syms: *[DataSym], |
| 44 | } |
| 45 | |
| 46 | /// Lay out data symbols for a single section. |
| 47 | /// Data with sidecar image bytes is placed first, then zero-initialized data, |
| 48 | /// so that only meaningful bytes need to be written to the output file. |
| 49 | /// Returns the updated offset past all placed symbols. |
| 50 | export fn layoutSection( |
| 51 | items: &[il::Data], |
| 52 | syms: &mut [DataSym], |
| 53 | count: &mut u32, |
| 54 | base: u32, |
| 55 | readOnly: bool |
| 56 | ) -> u32 throws (Error) { |
| 57 | return try layoutSectionAtOffset(items, syms, count, base, 0, readOnly); |
| 58 | } |
| 59 | |
| 60 | /// Lay out data symbols for a single section starting at [`startOffset`]. |
| 61 | export fn layoutSectionAtOffset( |
| 62 | items: &[il::Data], |
| 63 | syms: &mut [DataSym], |
| 64 | count: &mut u32, |
| 65 | base: u32, |
| 66 | startOffset: u32, |
| 67 | readOnly: bool |
| 68 | ) -> u32 throws (Error) { |
| 69 | let mut offset: u32 = startOffset; |
| 70 | |
| 71 | // Data requiring sidecar image bytes first. |
| 72 | for i in 0..items.len { |
| 73 | let data = items[i]; |
| 74 | if data.readOnly == readOnly and not data.isZeroInit { |
| 75 | if data.alignment == 0 or (data.alignment & (data.alignment - 1)) <> 0 { |
| 76 | throw Error::Alignment; |
| 77 | } |
| 78 | let aligned = (offset as u64 + data.alignment as u64 - 1) & ~(data.alignment as u64 - 1); |
| 79 | if aligned + data.size as u64 > 0xffffffff or base as u64 + aligned + data.size as u64 > 0xffffffff { |
| 80 | throw Error::Overflow; |
| 81 | } |
| 82 | if *count >= syms.len { |
| 83 | throw Error::Capacity; |
| 84 | } |
| 85 | set offset = aligned as u32; |
| 86 | set syms[*count] = DataSym { name: data.name, addr: base + offset }; |
| 87 | set *count += 1; |
| 88 | set offset += data.size; |
| 89 | } |
| 90 | } |
| 91 | // Zero-initialized data after. |
| 92 | for i in 0..items.len { |
| 93 | let data = items[i]; |
| 94 | if data.readOnly == readOnly and data.isZeroInit { |
| 95 | if data.alignment == 0 or (data.alignment & (data.alignment - 1)) <> 0 { |
| 96 | throw Error::Alignment; |
| 97 | } |
| 98 | let aligned = (offset as u64 + data.alignment as u64 - 1) & ~(data.alignment as u64 - 1); |
| 99 | if aligned + data.size as u64 > 0xffffffff or base as u64 + aligned + data.size as u64 > 0xffffffff { |
| 100 | throw Error::Overflow; |
| 101 | } |
| 102 | if *count >= syms.len { |
| 103 | throw Error::Capacity; |
| 104 | } |
| 105 | set offset = aligned as u32; |
| 106 | set syms[*count] = DataSym { name: data.name, addr: base + offset }; |
| 107 | set *count += 1; |
| 108 | set offset += data.size; |
| 109 | } |
| 110 | } |
| 111 | return offset; |
| 112 | } |
| 113 | |
| 114 | /// Emit data bytes for a single section (read-only or read-write) into `buf`. |
| 115 | /// Iterates data requiring sidecar image bytes, serializing each data item. |
| 116 | /// Returns the total number of bytes written. |
| 117 | export unsafe fn emitSection( |
| 118 | items: &[il::Data], |
| 119 | dataSymMap: &DataSymMap, |
| 120 | fnLabels: &labels::Labels, |
| 121 | codeBase: u32, |
| 122 | buf: &mut [u8], |
| 123 | readOnly: bool |
| 124 | ) -> u32 throws (Error) { |
| 125 | return try emitSectionAtOffset(items, dataSymMap, fnLabels, codeBase, buf, readOnly, 0); |
| 126 | } |
| 127 | |
| 128 | /// Emit data bytes for a single section starting at `startOffset`. |
| 129 | export unsafe fn emitSectionAtOffset( |
| 130 | items: &[il::Data], |
| 131 | dataSymMap: &DataSymMap, |
| 132 | fnLabels: &labels::Labels, |
| 133 | codeBase: u32, |
| 134 | buf: &mut [u8], |
| 135 | readOnly: bool, |
| 136 | startOffset: u32 |
| 137 | ) -> u32 throws (Error) { |
| 138 | if startOffset > buf.len { |
| 139 | throw Error::Capacity; |
| 140 | } |
| 141 | let mut offset: u32 = startOffset; |
| 142 | |
| 143 | for i in 0..items.len { |
| 144 | let data = items[i]; |
| 145 | if data.readOnly == readOnly and not data.isZeroInit { |
| 146 | if data.alignment == 0 or (data.alignment & (data.alignment - 1)) <> 0 { |
| 147 | throw Error::Alignment; |
| 148 | } |
| 149 | let aligned = (offset as u64 + data.alignment as u64 - 1) & ~(data.alignment as u64 - 1); |
| 150 | if aligned > buf.len as u64 or data.size as u64 > buf.len as u64 - aligned { |
| 151 | throw Error::Capacity; |
| 152 | } |
| 153 | set offset = aligned as u32; |
| 154 | let end = offset + data.size; |
| 155 | for j in 0..data.values.len { |
| 156 | let v = &data.values[j]; |
| 157 | let mut width: u32 = 1; |
| 158 | match v.item { |
| 159 | case il::DataItem::Val { typ, .. } => set width = il::typeSize(typ), |
| 160 | case il::DataItem::Sym(_), il::DataItem::Fn(_) => set width = 8, |
| 161 | case il::DataItem::Str(text) => set width = text.len, |
| 162 | case il::DataItem::Undef => {} |
| 163 | } |
| 164 | if width as u64 * v.count as u64 > (end - offset) as u64 { |
| 165 | throw Error::Capacity; |
| 166 | } |
| 167 | for _ in 0..v.count { |
| 168 | match v.item { |
| 169 | case il::DataItem::Val { typ, val } => { |
| 170 | let size = il::typeSize(typ); |
| 171 | try! mem::copy(&mut buf[offset..], @sliceOf(&val as &u8, size)); |
| 172 | |
| 173 | set offset += size; |
| 174 | }, |
| 175 | case il::DataItem::Sym(name) => { |
| 176 | let addr = lookupAddr(dataSymMap, name) else { |
| 177 | throw Error::Symbol; |
| 178 | }; |
| 179 | let addr64: u64 = addr as u64; |
| 180 | try! mem::copy(&mut buf[offset..], @sliceOf(&addr64 as &u8, 8)); |
| 181 | |
| 182 | set offset += @sizeOf(u64); |
| 183 | }, |
| 184 | case il::DataItem::Fn(name) => { |
| 185 | let functionOffset = dict::get(&fnLabels.funcs, name) else { |
| 186 | throw Error::Symbol; |
| 187 | }; |
| 188 | if functionOffset < 0 or codeBase as u64 + functionOffset as u64 > 0xffffffff { |
| 189 | throw Error::Overflow; |
| 190 | } |
| 191 | let addr64 = codeBase as u64 + functionOffset as u64; |
| 192 | try! mem::copy(&mut buf[offset..], @sliceOf(&addr64 as &u8, 8)); |
| 193 | |
| 194 | set offset += @sizeOf(*u8); |
| 195 | }, |
| 196 | case il::DataItem::Str(s) => { |
| 197 | try! mem::copy(&mut buf[offset..], s); |
| 198 | set offset += s.len; |
| 199 | }, |
| 200 | case il::DataItem::Undef => { |
| 201 | set buf[offset] = 0; |
| 202 | set offset += 1; |
| 203 | }, |
| 204 | } |
| 205 | } |
| 206 | } |
| 207 | } |
| 208 | } |
| 209 | return offset; |
| 210 | } |
| 211 | |
| 212 | /// Build a hash-indexed data symbol map from the laid-out symbols. |
| 213 | /// The table length must be a power of two and at least twice the symbol count. |
| 214 | /// The minimum table length is two. |
| 215 | export fn buildMap(syms: *[DataSym], entries: *mut [dict::Entry]) -> DataSymMap throws (Error) { |
| 216 | if entries.len < 2 or (entries.len & (entries.len - 1)) <> 0 or syms.len > entries.len / 2 { |
| 217 | throw Error::Capacity; |
| 218 | } |
| 219 | let mut d = dict::init(entries); |
| 220 | for i in 0..syms.len { |
| 221 | if syms[i].name.len == 0 or dict::get(&d, syms[i].name) <> nil { |
| 222 | throw Error::Symbol; |
| 223 | } |
| 224 | dict::insert(&mut d, syms[i].name, syms[i].addr as i32); |
| 225 | } |
| 226 | return DataSymMap { dict: d, syms }; |
| 227 | } |
| 228 | |
| 229 | /// Resolve a data symbol to its final absolute address using the hash map. |
| 230 | export fn lookupAddr(m: &DataSymMap, name: *[u8]) -> ?u32 { |
| 231 | if let v = dict::get(&m.dict, name) { |
| 232 | return v as u32; |
| 233 | } |
| 234 | return nil; |
| 235 | } |