Move data emission out of `rv64` module

e0f3345578702525c9dbd12409efa411b7e953bc0b8d68731c122febd00ae3d7
The code is architecture agnostic.
Alexis Sellier committed ago 1 parent 1c550ebe
compiler/radiance.rad +4 -2
13 13
use std::lang::il;
14 14
use std::lang::lower;
15 15
use std::arch::rv64;
16 16
use std::arch::rv64::printer;
17 17
use std::lang::sexpr;
18 +
use std::lang::gen::data;
19 +
use std::lang::gen::types;
18 20
use std::sys;
19 21
use std::sys::unix;
20 22
21 23
/// Maximum number of modules we can load per package.
22 24
const MAX_LOADED_MODULES: u32 = 64;
52 54
static RESOLVER_PKG_SCOPE: resolver::Scope = undefined;
53 55
/// Errors emitted by resolver.
54 56
static RESOLVER_ERRORS: [resolver::Error; resolver::MAX_ERRORS] = undefined;
55 57
56 58
/// Code generation storage.
57 -
static CODEGEN_DATA_SYMS: [rv64::DataSym; rv64::MAX_DATA_SYMS] = undefined;
59 +
static CODEGEN_DATA_SYMS: [data::DataSym; data::MAX_DATA_SYMS] = undefined;
58 60
59 61
/// Debug info file extension.
60 62
const DEBUG_EXT: *[u8] = ".debug";
61 63
62 64
/// Read-only data file extension.
699 701
700 702
/// Serialize debug entries and write the `.debug` file.
701 703
/// Resolves module IDs to file paths via the module graph.
702 704
/// Format per entry is `{pc: u32,  offset: u32, filePath: [u8], NULL}`.
703 705
fn writeDebugInfo(
704 -
    entries: *[rv64::emit::DebugEntry],
706 +
    entries: *[types::DebugEntry],
705 707
    graph: *module::ModuleGraph,
706 708
    basePath: *[u8],
707 709
    arena: *mut alloc::Arena
708 710
) throws (Error) {
709 711
    if entries.len == 0 {
lib/std/arch/rv64.rad +9 -140
21 21
use std::mem;
22 22
use std::lang::il;
23 23
use std::lang::alloc;
24 24
use std::lang::gen::labels;
25 25
use std::lang::gen::regalloc;
26 +
use std::lang::gen::data;
27 +
use std::lang::gen::types;
26 28
27 29
////////////////
28 30
// Registers  //
29 31
////////////////
30 32
140 142
141 143
///////////////////////
142 144
// Codegen Constants //
143 145
///////////////////////
144 146
145 -
/// Maximum number of data symbols.
146 -
pub const MAX_DATA_SYMS: u32 = 8192;
147 -
148 147
/// Base address where read-only data is loaded.
149 148
pub const RO_DATA_BASE: u32 = 0x10000;
150 149
151 150
/// Base address where read-write data is loaded.
152 151
pub const RW_DATA_BASE: u32 = 0xFFFFF0;
153 152
154 -
/// Data symbol entry mapping name to address.
155 -
pub record DataSym {
156 -
    /// Symbol name.
157 -
    name: *[u8],
158 -
    /// Absolute address, including data base address.
159 -
    addr: u32,
160 -
}
161 -
162 153
/// Storage buffers passed from driver for code generation.
163 154
pub record Storage {
164 155
    /// Buffer for data symbols.
165 -
    dataSyms: *mut [DataSym],
156 +
    dataSyms: *mut [data::DataSym],
166 157
}
167 158
168 159
/// Result of code generation.
169 160
pub record Program {
170 161
    /// Slice of emitted code.
171 162
    code: *[u32],
172 163
    /// Slice of function addresses (name + start index).
173 -
    funcs: *[emit::FuncAddr],
164 +
    funcs: *[types::FuncAddr],
174 165
    /// Number of read-only data bytes emitted.
175 166
    roDataSize: u32,
176 167
    /// Number of read-write data bytes emitted.
177 168
    rwDataSize: u32,
178 169
    /// Debug entries mapping PCs to source locations. Empty when debug is off.
179 -
    debugEntries: *[emit::DebugEntry],
180 -
}
181 -
182 -
/// Lay out data symbols for a single section.
183 -
/// Initialized data is placed first, then uninitialized, so that only
184 -
/// initialized data needs to be written to the output file.
185 -
/// Returns the updated offset past all placed symbols.
186 -
fn layoutSection(
187 -
    program: *il::Program,
188 -
    syms: *mut [DataSym],
189 -
    count: *mut u32,
190 -
    base: u32,
191 -
    readOnly: bool
192 -
) -> u32 {
193 -
    let mut offset: u32 = 0;
194 -
195 -
    // Initialized data first.
196 -
    for i in 0..program.data.len {
197 -
        let data = &program.data[i];
198 -
        if data.readOnly == readOnly and not data.isUndefined {
199 -
            offset = mem::alignUp(offset, data.alignment);
200 -
            syms[*count] = DataSym { name: data.name, addr: base + offset };
201 -
            *count += 1;
202 -
            offset += data.size;
203 -
        }
204 -
    }
205 -
    // Uninitialized data after.
206 -
    for i in 0..program.data.len {
207 -
        let data = &program.data[i];
208 -
        if data.readOnly == readOnly and data.isUndefined {
209 -
            offset = mem::alignUp(offset, data.alignment);
210 -
            syms[*count] = DataSym { name: data.name, addr: base + offset };
211 -
            *count += 1;
212 -
            offset += data.size;
213 -
        }
214 -
    }
215 -
    return offset;
216 -
}
217 -
218 -
/// Emit data bytes for a single section (read-only or read-write) into `buf`.
219 -
/// Iterates initialized data in the IL program, serializing each data item.
220 -
/// Returns the total number of bytes written.
221 -
pub fn emitSection(
222 -
    program: *il::Program,
223 -
    dataSyms: *[DataSym],
224 -
    fnLabels: *labels::Labels,
225 -
    codeBase: u32,
226 -
    buf: *mut [u8],
227 -
    readOnly: bool
228 -
) -> u32 {
229 -
    let mut offset: u32 = 0;
230 -
231 -
    for i in 0..program.data.len {
232 -
        let data = &program.data[i];
233 -
        if data.readOnly == readOnly and not data.isUndefined {
234 -
            offset = mem::alignUp(offset, data.alignment);
235 -
            if offset + data.size > buf.len {
236 -
                panic "emitSection: buffer overflow";
237 -
            }
238 -
            for j in 0..data.values.len {
239 -
                let v = &data.values[j];
240 -
                for _ in 0..v.count {
241 -
                    match v.item {
242 -
                        case il::DataItem::Val { typ, val } => {
243 -
                            let size = il::typeSize(typ);
244 -
                            let valPtr = &val as *u8;
245 -
                            try! mem::copy(&mut buf[offset..], @sliceOf(valPtr, size));
246 -
                            offset += size;
247 -
                        },
248 -
                        case il::DataItem::Sym(name) => {
249 -
                            let addr = lookupDataSymAddr(dataSyms, name) else {
250 -
                                panic "emitSection: data symbol not found";
251 -
                            };
252 -
                            // Write 8-byte pointer: low 4 bytes are the
253 -
                            // address, high 4 bytes are zero.
254 -
                            // TODO: Use `u64` once it's supported.
255 -
                            let lo: u32 = addr;
256 -
                            let hi: u32 = 0;
257 -
                            let loPtr = &lo as *u8;
258 -
                            let hiPtr = &hi as *u8;
259 -
260 -
                            try! mem::copy(&mut buf[offset..], @sliceOf(loPtr, 4));
261 -
                            try! mem::copy(&mut buf[(offset + 4)..], @sliceOf(hiPtr, 4));
262 -
263 -
                            offset += 8;
264 -
                        },
265 -
                        case il::DataItem::Fn(name) => {
266 -
                            let addr = codeBase + labels::funcOffset(fnLabels, name) as u32;
267 -
                            let lo: u32 = addr;
268 -
                            let hi: u32 = 0;
269 -
                            let loPtr = &lo as *u8;
270 -
                            let hiPtr = &hi as *u8;
271 -
272 -
                            try! mem::copy(&mut buf[offset..], @sliceOf(loPtr, 4));
273 -
                            try! mem::copy(&mut buf[(offset + 4)..], @sliceOf(hiPtr, 4));
274 -
275 -
                            offset += 8;
276 -
                        },
277 -
                        case il::DataItem::Str(s) => {
278 -
                            try! mem::copy(&mut buf[offset..], s);
279 -
                            offset += s.len;
280 -
                        },
281 -
                        case il::DataItem::Undef => {
282 -
                            buf[offset] = 0;
283 -
                            offset += 1;
284 -
                        },
285 -
                    }
286 -
                }
287 -
            }
288 -
        }
289 -
    }
290 -
    return offset;
291 -
}
292 -
293 -
/// Resolve a data symbol to its final absolute address.
294 -
fn lookupDataSymAddr(dataSyms: *[DataSym], name: *[u8]) -> ?u32 {
295 -
    for i in 0..dataSyms.len {
296 -
        let sym = dataSyms[i];
297 -
        if mem::eq(sym.name, name) {
298 -
            return sym.addr;
299 -
        }
300 -
    }
301 -
    return nil;
170 +
    debugEntries: *[types::DebugEntry],
302 171
}
303 172
304 173
/// Generate code for an IL program.
305 174
pub fn generate(
306 175
    program: *il::Program,
314 183
    let config = targetConfig();
315 184
316 185
    // Build data map.
317 186
    let mut dataSymCount: u32 = 0;
318 187
319 -
    let roLayoutSize = layoutSection(program, storage.dataSyms, &mut dataSymCount, RO_DATA_BASE, true);
320 -
    layoutSection(program, storage.dataSyms, &mut dataSymCount, RW_DATA_BASE, false);
188 +
    let roLayoutSize = data::layoutSection(program, storage.dataSyms, &mut dataSymCount, RO_DATA_BASE, true);
189 +
    data::layoutSection(program, storage.dataSyms, &mut dataSymCount, RW_DATA_BASE, false);
321 190
322 191
    // Code base address: code follows read-only data, aligned to dword boundary.
323 192
    let codeBase = mem::alignUp(RO_DATA_BASE + roLayoutSize, DWORD_SIZE as u32);
324 193
325 194
    // Emit placeholder entry jump to default function if there is one.
357 226
    // Patch function calls and address loads now that all functions are emitted.
358 227
    emit::patchCalls(&mut e);
359 228
    emit::patchAddrLoads(&mut e);
360 229
361 230
    // Emit data sections.
362 -
    let roDataSize = emitSection(program, dataSyms, &e.labels, codeBase, roDataBuf, true);
363 -
    let rwDataSize = emitSection(program, dataSyms, &e.labels, codeBase, rwDataBuf, false);
231 +
    let roDataSize = data::emitSection(program, dataSyms, &e.labels, codeBase, roDataBuf, true);
232 +
    let rwDataSize = data::emitSection(program, dataSyms, &e.labels, codeBase, rwDataBuf, false);
364 233
365 234
    return Program {
366 235
        code: emit::getCode(&e),
367 236
        funcs: emit::getFuncs(&e),
368 237
        roDataSize,
lib/std/arch/rv64/emit.rad +12 -29
3 3
//! Emits RV64 machine code as `u32` list.
4 4
5 5
use std::lang::il;
6 6
use std::lang::alloc;
7 7
use std::lang::gen::labels;
8 +
use std::lang::gen::types;
8 9
use std::collections::dict;
9 10
use std::mem;
10 11
11 12
use super::encode;
12 13
63 64
    target: *[u8],
64 65
    /// Destination register.
65 66
    rd: super::Reg,
66 67
}
67 68
68 -
/// Function address entry for printing.
69 -
pub record FuncAddr {
70 -
    /// Function name.
71 -
    name: *[u8],
72 -
    /// Instruction index where this function starts.
73 -
    index: u32,
74 -
}
75 -
76 -
/// Debug entry mapping an instruction to a source location.
77 -
pub record DebugEntry {
78 -
    /// Byte offset of the instruction from the start of the program.
79 -
    pc: u32,
80 -
    /// Module identifier.
81 -
    moduleId: u16,
82 -
    /// Byte offset into the module's source file.
83 -
    offset: u32,
84 -
}
85 -
86 69
/// Adjusted base register and offset for addressing.
87 70
pub record AdjustedOffset {
88 71
    /// Base register.
89 72
    base: super::Reg,
90 73
    /// Byte offset from register.
118 101
    /// Number of pending address loads.
119 102
    pendingAddrLoadsLen: u32,
120 103
    /// Block label tracking.
121 104
    labels: labels::Labels,
122 105
    /// Function start positions for printing.
123 -
    funcs: *mut [FuncAddr],
106 +
    funcs: *mut [types::FuncAddr],
124 107
    /// Number of recorded functions.
125 108
    funcsLen: u32,
126 109
    /// Debug entries mapping PCs to source locations.
127 -
    debugEntries: *mut [DebugEntry],
110 +
    debugEntries: *mut [types::DebugEntry],
128 111
    /// Number of debug entries recorded.
129 112
    debugEntriesLen: u32,
130 113
}
131 114
132 115
/// Computed stack frame layout for a function.
181 164
    let pendingBranches = try alloc::allocSlice(arena, @sizeOf(PendingBranch), @alignOf(PendingBranch), MAX_PENDING);
182 165
    let pendingCalls = try alloc::allocSlice(arena, @sizeOf(PendingCall), @alignOf(PendingCall), MAX_PENDING);
183 166
    let pendingAddrLoads = try alloc::allocSlice(arena, @sizeOf(PendingAddrLoad), @alignOf(PendingAddrLoad), MAX_PENDING);
184 167
    let blockOffsets = try alloc::allocSlice(arena, @sizeOf(i32), @alignOf(i32), labels::MAX_BLOCKS_PER_FN);
185 168
    let funcEntries = try alloc::allocSlice(arena, @sizeOf(dict::Entry), @alignOf(dict::Entry), labels::FUNC_TABLE_SIZE);
186 -
    let funcs = try alloc::allocSlice(arena, @sizeOf(FuncAddr), @alignOf(FuncAddr), MAX_FUNCS);
169 +
    let funcs = try alloc::allocSlice(arena, @sizeOf(types::FuncAddr), @alignOf(types::FuncAddr), MAX_FUNCS);
187 170
188 -
    let mut debugEntries: *mut [DebugEntry] = &mut [];
171 +
    let mut debugEntries: *mut [types::DebugEntry] = &mut [];
189 172
    if debug {
190 173
        debugEntries = try alloc::allocSlice(
191 -
            arena, @sizeOf(DebugEntry), @alignOf(DebugEntry), MAX_DEBUG_ENTRIES
192 -
        ) as *mut [DebugEntry];
174 +
            arena, @sizeOf(types::DebugEntry), @alignOf(types::DebugEntry), MAX_DEBUG_ENTRIES
175 +
        ) as *mut [types::DebugEntry];
193 176
    }
194 177
    return Emitter {
195 178
        code: code as *mut [u32],
196 179
        codeLen: 0,
197 180
        pendingBranches: pendingBranches as *mut [PendingBranch],
199 182
        pendingCalls: pendingCalls as *mut [PendingCall],
200 183
        pendingCallsLen: 0,
201 184
        pendingAddrLoads: pendingAddrLoads as *mut [PendingAddrLoad],
202 185
        pendingAddrLoadsLen: 0,
203 186
        labels: labels::init(blockOffsets as *mut [i32], funcEntries as *mut [dict::Entry]),
204 -
        funcs: funcs as *mut [FuncAddr],
187 +
        funcs: funcs as *mut [types::FuncAddr],
205 188
        funcsLen: 0,
206 189
        debugEntries,
207 190
        debugEntriesLen: 0,
208 191
    };
209 192
}
246 229
/// Record a function's start position for printing.
247 230
pub fn recordFunc(e: *mut Emitter, name: *[u8]) {
248 231
    if e.funcsLen >= e.funcs.len {
249 232
        panic "recordFunc: funcs buffer full";
250 233
    }
251 -
    e.funcs[e.funcsLen] = FuncAddr { name, index: e.codeLen };
234 +
    e.funcs[e.funcsLen] = types::FuncAddr { name, index: e.codeLen };
252 235
    e.funcsLen += 1;
253 236
}
254 237
255 238
/// Record a local branch needing later patching.
256 239
/// Emits two placeholder instructions that will be patched later.
660 643
pub fn getCode(e: *Emitter) -> *[u32] {
661 644
    return &e.code[..e.codeLen];
662 645
}
663 646
664 647
/// Get function addresses for printing.
665 -
pub fn getFuncs(e: *Emitter) -> *[FuncAddr] {
648 +
pub fn getFuncs(e: *Emitter) -> *[types::FuncAddr] {
666 649
    return &e.funcs[..e.funcsLen];
667 650
}
668 651
669 652
/// Record a debug entry mapping the current PC to a source location.
670 653
/// Deduplicates consecutive entries with the same location.
679 662
        }
680 663
    }
681 664
    if e.debugEntriesLen >= e.debugEntries.len {
682 665
        panic "recordSrcLoc: debug entry buffer full";
683 666
    }
684 -
    e.debugEntries[e.debugEntriesLen] = DebugEntry {
667 +
    e.debugEntries[e.debugEntriesLen] = types::DebugEntry {
685 668
        pc,
686 669
        moduleId: loc.moduleId,
687 670
        offset: loc.offset,
688 671
    };
689 672
    e.debugEntriesLen += 1;
690 673
}
691 674
692 675
/// Get debug entries as a slice.
693 -
pub fn getDebugEntries(e: *Emitter) -> *[DebugEntry] {
676 +
pub fn getDebugEntries(e: *Emitter) -> *[types::DebugEntry] {
694 677
    return &e.debugEntries[..e.debugEntriesLen];
695 678
}
lib/std/arch/rv64/isel.rad +7 -8
29 29
30 30
use std::mem;
31 31
use std::lang::il;
32 32
use std::lang::gen::regalloc;
33 33
use std::lang::gen::labels;
34 +
use std::lang::gen::data;
34 35
35 36
use super::encode;
36 37
use super::emit;
37 38
38 39
///////////////
79 80
    /// Emitter for outputting instructions.
80 81
    e: *mut emit::Emitter,
81 82
    /// Register allocation result.
82 83
    ralloc: *regalloc::AllocResult,
83 84
    /// Data symbols for resolving symbol addresses.
84 -
    dataSyms: *[super::DataSym],
85 +
    dataSyms: *[data::DataSym],
85 86
    /// Total stack frame size.
86 87
    frameSize: i32,
87 88
    /// Running offset into the reserve region of the frame.
88 89
    /// Tracks current position within the pre-allocated reserve slots.
89 90
    reserveOffset: i32,
135 136
    return getReg(s, ssa);
136 137
}
137 138
138 139
/// Look up symbol address in data map.
139 140
fn lookupDataSym(s: *Selector, name: *[u8]) -> u32 throws (SelectorError) {
140 -
    for sym in s.dataSyms {
141 -
        if mem::eq(sym.name, name) {
142 -
            return sym.addr;
143 -
        }
144 -
    }
145 -
    throw SelectorError::Internal;
141 +
    let addr = data::lookupDataSymAddr(s.dataSyms, name) else {
142 +
        throw SelectorError::Internal;
143 +
    };
144 +
    return addr;
146 145
}
147 146
148 147
/// Resolve an IL value to the physical register holding it.
149 148
/// For non-spilled register values, returns the physical register directly.
150 149
/// For immediates, symbols, and spilled registers, materializes into `scratch`.
249 248
}
250 249
251 250
/// Select instructions for a function.
252 251
pub fn selectFn(
253 252
    e: *mut emit::Emitter,
254 -
    dataSyms: *[super::DataSym],
253 +
    dataSyms: *[data::DataSym],
255 254
    ralloc: *regalloc::AllocResult,
256 255
    func: *il::Fn
257 256
) {
258 257
    // Reset block offsets for this function.
259 258
    labels::resetBlocks(&mut e.labels);
lib/std/arch/rv64/printer.rad +4 -3
4 4
5 5
use std::fmt;
6 6
use std::mem;
7 7
use std::lang::alloc;
8 8
use std::lang::sexpr;
9 +
use std::lang::gen::types;
9 10
10 11
use super::decode;
11 12
use super::emit;
12 13
13 14
/////////////////////
307 308
        },
308 309
    }
309 310
}
310 311
311 312
/// Print code with labels to the given output.
312 -
pub fn printCodeTo(out: *mut sexpr::Output, pkgName: *[u8], code: *[u32], funcs: *[emit::FuncAddr], arena: *mut alloc::Arena) {
313 +
pub fn printCodeTo(out: *mut sexpr::Output, pkgName: *[u8], code: *[u32], funcs: *[types::FuncAddr], arena: *mut alloc::Arena) {
313 314
    // Package header.
314 315
    write(out, "# package `");
315 316
    write(out, pkgName);
316 317
    write(out, "`\n\n");
317 318
325 326
        write(out, "\n");
326 327
    }
327 328
}
328 329
329 330
/// Find function at given instruction index.
330 -
fn findFunc(funcs: *[emit::FuncAddr], index: u32) -> ?*[u8] {
331 +
fn findFunc(funcs: *[types::FuncAddr], index: u32) -> ?*[u8] {
331 332
    for i in 0..funcs.len {
332 333
        if funcs[i].index == index {
333 334
            return funcs[i].name;
334 335
        }
335 336
    }
336 337
    return nil;
337 338
}
338 339
339 340
/// Print code with labels to buffer, returns slice.
340 -
pub fn printCode(pkgName: *[u8], code: *[u32], funcs: *[emit::FuncAddr], arena: *mut alloc::Arena, buf: *mut [u8]) -> *[u8] {
341 +
pub fn printCode(pkgName: *[u8], code: *[u32], funcs: *[types::FuncAddr], arena: *mut alloc::Arena, buf: *mut [u8]) -> *[u8] {
341 342
    let mut pos: u32 = 0;
342 343
    let mut out = sexpr::Output::Buffer { buf, pos: &mut pos };
343 344
    printCodeTo(&mut out, pkgName, code, funcs, arena);
344 345
345 346
    return &buf[..pos];
lib/std/lang/gen.rad +3 -1
1 1
//! Code generation framework.
2 2
//!
3 3
//! Target-independent infrastructure for lowering IL to machine code.
4 -
//! Target-specific backends (e.g., `std::arch::rv64::gen`) provide
4 +
//! Target-specific backends (e.g., `std::arch::rv64`) provide
5 5
//! instruction selection and emission.
6 6
7 7
pub mod labels;
8 8
pub mod bitset;
9 9
pub mod regalloc;
10 +
pub mod data;
11 +
pub mod types;
lib/std/lang/gen/data.rad added +141 -0
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::lang::il;
8 +
use std::lang::gen::labels;
9 +
10 +
/// Maximum number of data symbols.
11 +
pub const MAX_DATA_SYMS: u32 = 8192;
12 +
13 +
/// Data symbol entry mapping name to address.
14 +
pub record DataSym {
15 +
    /// Symbol name.
16 +
    name: *[u8],
17 +
    /// Absolute address, including data base address.
18 +
    addr: u32,
19 +
}
20 +
21 +
/// Lay out data symbols for a single section.
22 +
/// Initialized data is placed first, then uninitialized, so that only
23 +
/// initialized data needs to be written to the output file.
24 +
/// Returns the updated offset past all placed symbols.
25 +
pub fn layoutSection(
26 +
    program: *il::Program,
27 +
    syms: *mut [DataSym],
28 +
    count: *mut u32,
29 +
    base: u32,
30 +
    readOnly: bool
31 +
) -> u32 {
32 +
    let mut offset: u32 = 0;
33 +
34 +
    // Initialized data first.
35 +
    for i in 0..program.data.len {
36 +
        let data = &program.data[i];
37 +
        if data.readOnly == readOnly and not data.isUndefined {
38 +
            offset = mem::alignUp(offset, data.alignment);
39 +
            syms[*count] = DataSym { name: data.name, addr: base + offset };
40 +
            *count += 1;
41 +
            offset += data.size;
42 +
        }
43 +
    }
44 +
    // Uninitialized data after.
45 +
    for i in 0..program.data.len {
46 +
        let data = &program.data[i];
47 +
        if data.readOnly == readOnly and data.isUndefined {
48 +
            offset = mem::alignUp(offset, data.alignment);
49 +
            syms[*count] = DataSym { name: data.name, addr: base + offset };
50 +
            *count += 1;
51 +
            offset += data.size;
52 +
        }
53 +
    }
54 +
    return offset;
55 +
}
56 +
57 +
/// Emit data bytes for a single section (read-only or read-write) into `buf`.
58 +
/// Iterates initialized data in the IL program, serializing each data item.
59 +
/// Returns the total number of bytes written.
60 +
pub fn emitSection(
61 +
    program: *il::Program,
62 +
    dataSyms: *[DataSym],
63 +
    fnLabels: *labels::Labels,
64 +
    codeBase: u32,
65 +
    buf: *mut [u8],
66 +
    readOnly: bool
67 +
) -> u32 {
68 +
    let mut offset: u32 = 0;
69 +
70 +
    for i in 0..program.data.len {
71 +
        let data = &program.data[i];
72 +
        if data.readOnly == readOnly and not data.isUndefined {
73 +
            offset = mem::alignUp(offset, data.alignment);
74 +
            if offset + data.size > buf.len {
75 +
                panic "emitSection: buffer overflow";
76 +
            }
77 +
            for j in 0..data.values.len {
78 +
                let v = &data.values[j];
79 +
                for _ in 0..v.count {
80 +
                    match v.item {
81 +
                        case il::DataItem::Val { typ, val } => {
82 +
                            let size = il::typeSize(typ);
83 +
                            let valPtr = &val as *u8;
84 +
                            try! mem::copy(&mut buf[offset..], @sliceOf(valPtr, size));
85 +
                            offset += size;
86 +
                        },
87 +
                        case il::DataItem::Sym(name) => {
88 +
                            let addr = lookupDataSymAddr(dataSyms, name) else {
89 +
                                panic "emitSection: data symbol not found";
90 +
                            };
91 +
                            // Write 8-byte pointer: low 4 bytes are the
92 +
                            // address, high 4 bytes are zero.
93 +
                            // TODO: Use `u64` once it's supported.
94 +
                            let lo: u32 = addr;
95 +
                            let hi: u32 = 0;
96 +
                            let loPtr = &lo as *u8;
97 +
                            let hiPtr = &hi as *u8;
98 +
99 +
                            try! mem::copy(&mut buf[offset..], @sliceOf(loPtr, 4));
100 +
                            try! mem::copy(&mut buf[(offset + 4)..], @sliceOf(hiPtr, 4));
101 +
102 +
                            offset += 8;
103 +
                        },
104 +
                        case il::DataItem::Fn(name) => {
105 +
                            let addr = codeBase + labels::funcOffset(fnLabels, name) as u32;
106 +
                            let lo: u32 = addr;
107 +
                            let hi: u32 = 0;
108 +
                            let loPtr = &lo as *u8;
109 +
                            let hiPtr = &hi as *u8;
110 +
111 +
                            try! mem::copy(&mut buf[offset..], @sliceOf(loPtr, 4));
112 +
                            try! mem::copy(&mut buf[(offset + 4)..], @sliceOf(hiPtr, 4));
113 +
114 +
                            offset += 8;
115 +
                        },
116 +
                        case il::DataItem::Str(s) => {
117 +
                            try! mem::copy(&mut buf[offset..], s);
118 +
                            offset += s.len;
119 +
                        },
120 +
                        case il::DataItem::Undef => {
121 +
                            buf[offset] = 0;
122 +
                            offset += 1;
123 +
                        },
124 +
                    }
125 +
                }
126 +
            }
127 +
        }
128 +
    }
129 +
    return offset;
130 +
}
131 +
132 +
/// Resolve a data symbol to its final absolute address.
133 +
pub fn lookupDataSymAddr(dataSyms: *[DataSym], name: *[u8]) -> ?u32 {
134 +
    for i in 0..dataSyms.len {
135 +
        let sym = dataSyms[i];
136 +
        if mem::eq(sym.name, name) {
137 +
            return sym.addr;
138 +
        }
139 +
    }
140 +
    return nil;
141 +
}
lib/std/lang/gen/types.rad added +21 -0
1 +
//! Target-independent code generation types.
2 +
//!
3 +
//! Defines common records used by all code generation backends.
4 +
5 +
/// Function address entry for printing.
6 +
pub record FuncAddr {
7 +
    /// Function name.
8 +
    name: *[u8],
9 +
    /// Instruction index where this function starts.
10 +
    index: u32,
11 +
}
12 +
13 +
/// Debug entry mapping an instruction to a source location.
14 +
pub record DebugEntry {
15 +
    /// Byte offset of the instruction from the start of the program.
16 +
    pc: u32,
17 +
    /// Module identifier.
18 +
    moduleId: u16,
19 +
    /// Byte offset into the module's source file.
20 +
    offset: u32,
21 +
}
std.lib +2 -0
32 32
lib/std/lang/module/printer.rad
33 33
lib/std/lang/package.rad
34 34
lib/std/lang/gen.rad
35 35
lib/std/lang/gen/labels.rad
36 36
lib/std/lang/gen/bitset.rad
37 +
lib/std/lang/gen/data.rad
38 +
lib/std/lang/gen/types.rad
37 39
lib/std/lang/gen/regalloc.rad
38 40
lib/std/lang/gen/regalloc/liveness.rad
39 41
lib/std/lang/gen/regalloc/spill.rad
40 42
lib/std/lang/gen/regalloc/assign.rad