compiler/
lib/
examples/
std/
arch/
char/
collections/
graph/
lang/
alloc/
ast/
gen/
bitset/
regalloc/
bitset.rad
4.6 KiB
data.rad
8.6 KiB
labels.rad
2.4 KiB
regalloc.rad
2.4 KiB
types.rad
594 B
il/
module/
parser/
resolver/
scanner/
alloc.rad
7.1 KiB
ast.rad
26.9 KiB
gen.rad
513 B
il.rad
20.4 KiB
lower.rad
321.7 KiB
module.rad
17.3 KiB
package.rad
1.3 KiB
parser.rad
92.2 KiB
resolver.rad
511.1 KiB
scanner.rad
17.9 KiB
sexpr.rad
6.7 KiB
strings.rad
2.2 KiB
types.rad
1.6 KiB
sys/
arch.rad
68 B
char.rad
855 B
collections.rad
39 B
fmt.rad
8.3 KiB
graph.rad
4.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
299 B
scripts/
seed/
sublime/
test/
vim/
.gitignore
336 B
.gitsigners
112 B
CELL_PERMISSIONS
6.8 KiB
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
5.4 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
808 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 | /// A data section cannot fit its address or symbol storage. |
| 19 | export union Error: Copy { |
| 20 | /// A required data or function symbol is missing or duplicated. |
| 21 | Symbol, |
| 22 | /// The symbol array is full. |
| 23 | Capacity, |
| 24 | /// A section size or address would overflow. |
| 25 | Overflow, |
| 26 | /// A data 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: u64, |
| 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: u64, |
| 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: u64, |
| 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 | try place(&data, syms, count, base, &mut offset); |
| 76 | } |
| 77 | } |
| 78 | // Zero-initialized data after. |
| 79 | for i in 0..items.len { |
| 80 | let data = items[i]; |
| 81 | if data.readOnly == readOnly and data.isZeroInit { |
| 82 | try place(&data, syms, count, base, &mut offset); |
| 83 | } |
| 84 | } |
| 85 | return offset; |
| 86 | } |
| 87 | |
| 88 | /// Place a data symbol with checked offset and address arithmetic. |
| 89 | fn place(item: &il::Data, syms: &mut [DataSym], count: &mut u32, base: u64, offset: &mut u32) |
| 90 | throws (Error) |
| 91 | { |
| 92 | if item.alignment == 0 or (item.alignment & (item.alignment - 1)) <> 0 { |
| 93 | throw Error::Alignment; |
| 94 | } |
| 95 | if (base & (item.alignment as u64 - 1)) <> 0 { |
| 96 | throw Error::Alignment; |
| 97 | } |
| 98 | let aligned = (*offset as u64 + item.alignment as u64 - 1) & ~(item.alignment as u64 - 1); |
| 99 | let end = aligned + item.size as u64; |
| 100 | if end > 0xffffffff or base > 0xffffffffffffffff - end { |
| 101 | throw Error::Overflow; |
| 102 | } |
| 103 | if *count >= syms.len { |
| 104 | throw Error::Capacity; |
| 105 | } |
| 106 | set syms[*count] = DataSym { name: item.name, addr: base + aligned }; |
| 107 | set *count += 1; |
| 108 | set *offset = end as u32; |
| 109 | } |
| 110 | |
| 111 | /// Emit data bytes for a single section (read-only or read-write) into `buf`. |
| 112 | /// Iterates data requiring sidecar image bytes, serializing each data item. |
| 113 | /// Returns the total number of bytes written. |
| 114 | export fn emitSection( |
| 115 | items: &[il::Data], |
| 116 | dataSymMap: &DataSymMap, |
| 117 | fnLabels: &labels::Labels, |
| 118 | codeBase: u64, |
| 119 | buf: &mut [u8], |
| 120 | readOnly: bool |
| 121 | ) -> u32 throws (Error) { |
| 122 | return try emitSectionAtOffset(items, dataSymMap, fnLabels, codeBase, buf, readOnly, 0); |
| 123 | } |
| 124 | |
| 125 | /// Emit data bytes for a single section starting at `startOffset`. |
| 126 | export fn emitSectionAtOffset( |
| 127 | items: &[il::Data], |
| 128 | dataSymMap: &DataSymMap, |
| 129 | fnLabels: &labels::Labels, |
| 130 | codeBase: u64, |
| 131 | buf: &mut [u8], |
| 132 | readOnly: bool, |
| 133 | startOffset: u32 |
| 134 | ) -> u32 throws (Error) { |
| 135 | let mut offset: u32 = startOffset; |
| 136 | if offset > buf.len { |
| 137 | throw Error::Capacity; |
| 138 | } |
| 139 | |
| 140 | for i in 0..items.len { |
| 141 | let data = items[i]; |
| 142 | if data.readOnly == readOnly and not data.isZeroInit { |
| 143 | let start = offset; |
| 144 | if data.alignment == 0 or (data.alignment & (data.alignment - 1)) <> 0 { |
| 145 | throw Error::Alignment; |
| 146 | } |
| 147 | let aligned = (offset as u64 + data.alignment as u64 - 1) & ~(data.alignment as u64 - 1); |
| 148 | if aligned > buf.len as u64 { |
| 149 | throw Error::Capacity; |
| 150 | } |
| 151 | set offset = aligned as u32; |
| 152 | if data.size > buf.len - offset { |
| 153 | throw Error::Capacity; |
| 154 | } |
| 155 | let end = offset + data.size; |
| 156 | for j in start..end { |
| 157 | set buf[j] = 0; |
| 158 | } |
| 159 | for j in 0..data.values.len { |
| 160 | let v = &data.values[j]; |
| 161 | let mut width: u32 = 1; |
| 162 | match v.item { |
| 163 | case il::DataItem::Val { typ, .. } => { |
| 164 | set width = il::typeSize(typ); |
| 165 | }, |
| 166 | case il::DataItem::Sym(_), il::DataItem::Fn(_) => { |
| 167 | set width = 8; |
| 168 | }, |
| 169 | case il::DataItem::Str(bytes) => { |
| 170 | set width = bytes.len; |
| 171 | }, |
| 172 | else => { |
| 173 | }, |
| 174 | } |
| 175 | if width > 0 and v.count > (end - offset) / width { |
| 176 | throw Error::Overflow; |
| 177 | } |
| 178 | if width == 0 { |
| 179 | continue; |
| 180 | } |
| 181 | for _ in 0..v.count { |
| 182 | match v.item { |
| 183 | case il::DataItem::Val { typ, val } => { |
| 184 | let size = il::typeSize(typ); |
| 185 | writeInteger(&mut buf[offset..offset + size], val as u64); |
| 186 | |
| 187 | set offset += size; |
| 188 | }, |
| 189 | case il::DataItem::Sym(name) => { |
| 190 | let addr = lookupAddr(dataSymMap, name) else { |
| 191 | throw Error::Symbol; |
| 192 | }; |
| 193 | writeInteger(&mut buf[offset..offset + 8], addr); |
| 194 | |
| 195 | set offset += @sizeOf(u64); |
| 196 | }, |
| 197 | case il::DataItem::Fn(name) => { |
| 198 | let fnOffset = dict::get(&fnLabels.funcs, name) else { |
| 199 | throw Error::Symbol; |
| 200 | }; |
| 201 | if fnOffset < 0 or codeBase > 0xffffffffffffffff - fnOffset as u64 { |
| 202 | throw Error::Overflow; |
| 203 | } |
| 204 | let addr = codeBase + fnOffset as u64; |
| 205 | writeInteger(&mut buf[offset..offset + 8], addr); |
| 206 | |
| 207 | set offset += @sizeOf(u64); |
| 208 | }, |
| 209 | case il::DataItem::Str(s) => { |
| 210 | try! mem::copy(&mut buf[offset..], s); |
| 211 | set offset += s.len; |
| 212 | }, |
| 213 | case il::DataItem::Undef => { |
| 214 | set buf[offset] = 0; |
| 215 | set offset += 1; |
| 216 | }, |
| 217 | } |
| 218 | } |
| 219 | } |
| 220 | set offset = end; |
| 221 | } |
| 222 | } |
| 223 | return offset; |
| 224 | } |
| 225 | |
| 226 | /// Write the low bytes of an integer in little-endian order. |
| 227 | fn writeInteger(buf: &mut [u8], value: u64) { |
| 228 | assert buf.len <= 8; |
| 229 | for i in 0..buf.len { |
| 230 | set buf[i] = (value >> (i * 8) as u64) as u8; |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | /// Build a hash-indexed data symbol map from the laid-out symbols. |
| 235 | /// The entry count must be a power of two, at least twice the symbol count. |
| 236 | export fn buildMap(syms: *[DataSym], entries: *mut [dict::Entry]) -> DataSymMap throws (Error) { |
| 237 | if entries.len == 0 or (entries.len & (entries.len - 1)) <> 0 or syms.len > entries.len / 2 { |
| 238 | throw Error::Capacity; |
| 239 | } |
| 240 | let mut d = dict::init(entries); |
| 241 | for i in 0..syms.len { |
| 242 | if syms[i].name.len == 0 or dict::get(&d, syms[i].name) <> nil { |
| 243 | throw Error::Symbol; |
| 244 | } |
| 245 | dict::insert(&mut d, syms[i].name, i as i32); |
| 246 | } |
| 247 | return DataSymMap { dict: d, syms }; |
| 248 | } |
| 249 | |
| 250 | /// Resolve a data symbol to its final absolute address using the hash map. |
| 251 | export fn lookupAddr(m: &DataSymMap, name: *[u8]) -> ?u64 { |
| 252 | if let v = dict::get(&m.dict, name) { |
| 253 | return m.syms[v as u32].addr; |
| 254 | } |
| 255 | return nil; |
| 256 | } |