lib/std/lang/gen/data.rad 5.9 KiB 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 symbol entry mapping name to address.
19
export record DataSym: Copy {
20
    /// Symbol name.
21
    name: *[u8],
22
    /// Absolute address, including data base address.
23
    addr: u32,
24
}
25
26
/// Hash-indexed data symbol map.
27
export record DataSymMap: Copy {
28
    /// Underlying hash table.
29
    dict: dict::Dict,
30
    /// Fallback linear array for edge cases.
31
    syms: *[DataSym],
32
}
33
34
/// Lay out data symbols for a single section.
35
/// Data with sidecar image bytes is placed first, then zero-initialized data,
36
/// so that only meaningful bytes need to be written to the output file.
37
/// Returns the updated offset past all placed symbols.
38
export fn layoutSection(
39
    items: *[il::Data],
40
    syms: *mut [DataSym],
41
    count: *mut u32,
42
    base: u32,
43
    readOnly: bool
44
) -> u32 {
45
    return layoutSectionAtOffset(items, syms, count, base, 0, readOnly);
46
}
47
48
/// Lay out data symbols for a single section starting at [`startOffset`].
49
export fn layoutSectionAtOffset(
50
    items: *[il::Data],
51
    syms: *mut [DataSym],
52
    count: *mut u32,
53
    base: u32,
54
    startOffset: u32,
55
    readOnly: bool
56
) -> u32 {
57
    let mut offset: u32 = startOffset;
58
59
    // Data requiring sidecar image bytes first.
60
    for i in 0..items.len {
61
        let data = &items[i];
62
        if data.readOnly == readOnly and not data.isZeroInit {
63
            set offset = mem::alignUp(offset, data.alignment);
64
            set syms[*count] = DataSym { name: data.name, addr: base + offset };
65
            set *count += 1;
66
            set offset += data.size;
67
        }
68
    }
69
    // Zero-initialized data after.
70
    for i in 0..items.len {
71
        let data = &items[i];
72
        if data.readOnly == readOnly and data.isZeroInit {
73
            set offset = mem::alignUp(offset, data.alignment);
74
            set syms[*count] = DataSym { name: data.name, addr: base + offset };
75
            set *count += 1;
76
            set offset += data.size;
77
        }
78
    }
79
    return offset;
80
}
81
82
/// Emit data bytes for a single section (read-only or read-write) into `buf`.
83
/// Iterates data requiring sidecar image bytes, serializing each data item.
84
/// Returns the total number of bytes written.
85
export fn emitSection(
86
    items: *[il::Data],
87
    dataSymMap: *DataSymMap,
88
    fnLabels: *labels::Labels,
89
    codeBase: u32,
90
    buf: *mut [u8],
91
    readOnly: bool
92
) -> u32 {
93
    return emitSectionAtOffset(items, dataSymMap, fnLabels, codeBase, buf, readOnly, 0);
94
}
95
96
/// Emit data bytes for a single section starting at `startOffset`.
97
export fn emitSectionAtOffset(
98
    items: *[il::Data],
99
    dataSymMap: *DataSymMap,
100
    fnLabels: *labels::Labels,
101
    codeBase: u32,
102
    buf: *mut [u8],
103
    readOnly: bool,
104
    startOffset: u32
105
) -> u32 {
106
    let mut offset: u32 = startOffset;
107
108
    for i in 0..items.len {
109
        let data = &items[i];
110
        if data.readOnly == readOnly and not data.isZeroInit {
111
            set offset = mem::alignUp(offset, data.alignment);
112
            assert offset + data.size <= buf.len, "emitSectionAtOffset: buffer overflow";
113
            for j in 0..data.values.len {
114
                let v = &data.values[j];
115
                for _ in 0..v.count {
116
                    match v.item {
117
                        case il::DataItem::Val { typ, val } => {
118
                            let size = il::typeSize(typ);
119
                            let valPtr = &val as *u8;
120
                            try! mem::copy(&mut buf[offset..], @sliceOf(valPtr, size));
121
122
                            set offset += size;
123
                        },
124
                        case il::DataItem::Sym(name) => {
125
                            let addr = lookupAddr(dataSymMap, name) else {
126
                                panic "emitSectionAtOffset: data symbol not found";
127
                            };
128
                            let addr64: u64 = addr as u64;
129
                            let addrPtr = &addr64 as *u8;
130
131
                            try! mem::copy(&mut buf[offset..], @sliceOf(addrPtr, 8));
132
133
                            set offset += @sizeOf(u64);
134
                        },
135
                        case il::DataItem::Fn(name) => {
136
                            let addr = codeBase + labels::funcOffset(fnLabels, name) as u32;
137
                            let addr64: u64 = addr as u64;
138
                            let addrPtr = &addr64 as *u8;
139
140
                            try! mem::copy(&mut buf[offset..], @sliceOf(addrPtr, 8));
141
142
                            set offset += @sizeOf(*u8);
143
                        },
144
                        case il::DataItem::Str(s) => {
145
                            try! mem::copy(&mut buf[offset..], s);
146
                            set offset += s.len;
147
                        },
148
                        case il::DataItem::Undef => {
149
                            set buf[offset] = 0;
150
                            set offset += 1;
151
                        },
152
                    }
153
                }
154
            }
155
        }
156
    }
157
    return offset;
158
}
159
160
/// Build a hash-indexed data symbol map from the laid-out symbols.
161
/// The `entries` slice must have length `DATA_SYM_TABLE_SIZE`.
162
export fn buildMap(syms: *[DataSym], entries: *mut [dict::Entry]) -> DataSymMap {
163
    let mut d = dict::init(entries);
164
    for i in 0..syms.len {
165
        dict::insert(&mut d, syms[i].name, syms[i].addr as i32);
166
    }
167
    return DataSymMap { dict: d, syms };
168
}
169
170
/// Resolve a data symbol to its final absolute address using the hash map.
171
export fn lookupAddr(m: *DataSymMap, name: *[u8]) -> ?u32 {
172
    if let v = dict::get(&m.dict, name) {
173
        return v as u32;
174
    }
175
    return nil;
176
}