lib/std/lang/il/binary/collect.rad 9.5 KiB raw
1
//! Deterministic symbol and dependency collection for package-local IL.
2
3
use std::mem;
4
use std::lang::il;
5
use std::lang::il::binary;
6
7
/// Caller-owned storage for a package's indexed names.
8
/// Both tables are exclusively borrowed for the collector's region.
9
export record Names: 'tables {
10
    /// Unique names in first-use order.
11
    symbols: &'tables mut [*[u8]],
12
    /// Number of initialized symbol entries.
13
    symbolCount: u32,
14
    /// Unique dependency names in first-use order.
15
    dependencies: &'tables mut [*[u8]],
16
    /// Number of initialized dependency entries.
17
    dependencyCount: u32,
18
}
19
20
/// Start collection with empty caller-provided tables.
21
/// Retain both tables for the same region.
22
export fn new 'tables (symbols: &'tables mut [*[u8]], dependencies: &'tables mut [*[u8]]) -> Names 'tables {
23
    return Names 'tables { symbols, symbolCount: 0, dependencies, dependencyCount: 0 };
24
}
25
26
/// Add a unique name and check the symbol table's capacity.
27
fn add 'tables (names: &mut Names 'tables, name: *[u8]) throws (binary::Error) {
28
    if name.len == 0 {
29
        throw binary::Error::Invalid;
30
    }
31
    for i in 0..names.symbolCount {
32
        if mem::eq(names.symbols[i], name) {
33
            return;
34
        }
35
    }
36
    if names.symbolCount == names.symbols.len {
37
        throw binary::Error::Capacity;
38
    }
39
    set names.symbols[names.symbolCount] = name;
40
    set names.symbolCount += 1;
41
}
42
43
/// Add a qualified reference and its owning package dependency.
44
fn reference 'tables (names: &mut Names 'tables, owner: *[u8], name: *[u8]) throws (binary::Error) {
45
    try add(names, name);
46
    for i in 0..name.len {
47
        if name[i] == ':' and i + 1 < name.len and name[i + 1] == ':' {
48
            if i == 0 or i + 2 == name.len {
49
                throw binary::Error::Invalid;
50
            }
51
            let dependency = &name[..i];
52
            if mem::eq(dependency, owner) {
53
                return;
54
            }
55
            try add(names, dependency);
56
            for j in 0..names.dependencyCount {
57
                if mem::eq(names.dependencies[j], dependency) {
58
                    return;
59
                }
60
            }
61
            if names.dependencyCount == names.dependencies.len {
62
                throw binary::Error::Capacity;
63
            }
64
            set names.dependencies[names.dependencyCount] = dependency;
65
            set names.dependencyCount += 1;
66
            return;
67
        }
68
    }
69
    throw binary::Error::Invalid;
70
}
71
72
/// Collect names referenced by one IL value.
73
fn value 'tables (names: &mut Names 'tables, owner: *[u8], item: il::Val) throws (binary::Error) {
74
    match item {
75
        case il::Val::DataSym(name) => try reference(names, owner, name),
76
        case il::Val::FnAddr(name) => try reference(names, owner, name),
77
        else => {
78
        },
79
    }
80
}
81
82
/// Collect names from an instruction's variable-length operand sequence.
83
fn values 'tables (names: &mut Names 'tables, owner: *[u8], items: &[il::Val]) throws (binary::Error) {
84
    for item in items {
85
        try value(names, owner, item);
86
    }
87
}
88
89
/// Collect all symbolic instruction operands.
90
fn instruction 'tables (names: &mut Names 'tables, owner: *[u8], instr: &il::Instr) throws (binary::Error) {
91
    match instr {
92
        case il::Instr::Call { func, args, .. } => {
93
            try value(names, owner, *func);
94
            unsafe {
95
                try values(names, owner, *args);
96
            }
97
        },
98
        case il::Instr::Jmp { args, .. } => {
99
            unsafe {
100
                try values(names, owner, *args);
101
            }
102
        },
103
        case il::Instr::Br { a, b, thenArgs, elseArgs, .. } => {
104
            try value(names, owner, *a);
105
            try value(names, owner, *b);
106
            unsafe {
107
                try values(names, owner, *thenArgs);
108
                try values(names, owner, *elseArgs);
109
            }
110
        },
111
        case il::Instr::Switch { val, defaultArgs, cases, .. } => {
112
            try value(names, owner, *val);
113
            unsafe {
114
                try switchValues(names, owner, *defaultArgs, *cases);
115
            }
116
        },
117
        else => try fixedInstruction(names, owner, instr),
118
    }
119
}
120
121
/// Collect default arguments before the arguments of each switch case.
122
fn switchValues 'tables (names: &mut Names 'tables, owner: *[u8], defaultArgs: &[il::Val], cases: &[il::SwitchCase]) throws (binary::Error) {
123
    try values(names, owner, defaultArgs);
124
    for i in 0..cases.len {
125
        let branch = &cases[i];
126
        unsafe {
127
            try values(names, owner, branch.args);
128
        }
129
    }
130
}
131
132
/// Collect symbolic operands stored directly in an instruction.
133
fn fixedInstruction 'tables (names: &mut Names 'tables, owner: *[u8], instr: &il::Instr) throws (binary::Error) {
134
    match *instr {
135
        case il::Instr::Reserve { size, .. } => try value(names, owner, size),
136
        case il::Instr::Blit { size, .. } => try value(names, owner, size),
137
        case il::Instr::Store { src, .. } => try value(names, owner, src),
138
        case il::Instr::Copy { val, .. } => try value(names, owner, val),
139
        case il::Instr::Zext { val, .. } => try value(names, owner, val),
140
        case il::Instr::Sext { val, .. } => try value(names, owner, val),
141
        case il::Instr::BinOp { a, b, .. } => {
142
            try value(names, owner, a);
143
            try value(names, owner, b);
144
        },
145
        case il::Instr::UnOp { a, .. } => try value(names, owner, a),
146
        case il::Instr::Ret { val } => {
147
            if let item = val {
148
                try value(names, owner, item);
149
            }
150
        },
151
        case il::Instr::Ecall { num, a0, a1, a2, a3, .. } => {
152
            try value(names, owner, num);
153
            try value(names, owner, a0);
154
            try value(names, owner, a1);
155
            try value(names, owner, a2);
156
            try value(names, owner, a3);
157
        },
158
        case il::Instr::DeviceRead { handle, offset, .. } => {
159
            try value(names, owner, handle); try value(names, owner, offset);
160
        },
161
        case il::Instr::DeviceWrite { handle, offset, value: source, .. } => {
162
            try value(names, owner, handle); try value(names, owner, offset); try value(names, owner, source);
163
        },
164
        case il::Instr::Load { .. }, il::Instr::Sload { .. }, il::Instr::Unreachable,
165
             il::Instr::Ebreak, il::Instr::MemoryFence => {
166
             },
167
        case il::Instr::Call { .. }, il::Instr::Jmp { .. }, il::Instr::Br { .. }, il::Instr::Switch { .. } =>
168
            panic "fixedInstruction: expected inline operands",
169
    }
170
}
171
172
/// Check that a definition belongs to the selected package.
173
fn definition 'tables (names: &mut Names 'tables, owner: *[u8], name: *[u8]) throws (binary::Error) {
174
    let rest = mem::stripPrefix(owner, name) else {
175
        throw binary::Error::Invalid;
176
    };
177
    if rest.len < 3 or rest[0] <> ':' or rest[1] <> ':' {
178
        throw binary::Error::Invalid;
179
    }
180
    try add(names, name);
181
}
182
183
/// Collect definitions and symbolic initializers from global data.
184
fn collectData 'tables (names: &mut Names 'tables, owner: *[u8], items: &[il::Data]) throws (binary::Error) {
185
    for item in items {
186
        try definition(names, owner, item.name);
187
        for data in item.values {
188
            match data.item {
189
                case il::DataItem::Sym(name) => try reference(names, owner, name),
190
                case il::DataItem::Fn(name) => try reference(names, owner, name),
191
                else => {
192
                },
193
            }
194
        }
195
    }
196
}
197
198
/// Collect function definitions and their bodies in pointer-table order.
199
fn collectFunctions 'tables (names: &mut Names 'tables, owner: *[u8], functions: &[*unsafe il::Fn]) throws (binary::Error) {
200
    for i in 0..functions.len {
201
        let func = functions[i];
202
        unsafe {
203
            try collectFunction(names, owner, func.name, func.blocks);
204
        }
205
    }
206
}
207
208
/// Collect a function definition before its block references.
209
fn collectFunction 'tables (names: &mut Names 'tables, owner: *[u8], name: *[u8], blocks: &[il::Block]) throws (binary::Error) {
210
    try definition(names, owner, name);
211
    for i in 0..blocks.len {
212
        let block = &blocks[i];
213
        unsafe {
214
            try collectInstructions(names, owner, block.instrs);
215
        }
216
    }
217
}
218
219
/// Collect instruction references in table order.
220
fn collectInstructions 'tables (names: &mut Names 'tables, owner: *[u8], instructions: &[il::Instr]) throws (binary::Error) {
221
    for i in 0..instructions.len {
222
        try instruction(names, owner, &instructions[i]);
223
    }
224
}
225
226
/// Build package tables from local definitions and their qualified references.
227
/// Returned tables borrow `names`. Reset the collector before building another package.
228
export unsafe fn package 'tables (
229
    names: &mut Names 'tables,
230
    owner: *[u8],
231
    program: il::Program,
232
    exports: &[binary::Export],
233
    entry: ?*[u8]
234
) -> binary::Package throws (binary::Error) {
235
    try collectNames(names, owner, &program, exports, entry);
236
    return binary::Package {
237
        symbols: &names.symbols[..names.symbolCount], name: owner,
238
        dependencies: &names.dependencies[..names.dependencyCount], exports: exports as *unsafe [binary::Export], entry, program,
239
    };
240
}
241
242
/// Collect package metadata before data and function references.
243
fn collectNames 'tables (
244
    names: &mut Names 'tables,
245
    owner: *[u8],
246
    program: &il::Program,
247
    exports: &[binary::Export],
248
    entry: ?*[u8],
249
) throws (binary::Error) {
250
    try add(names, owner);
251
    for item in exports {
252
        try definition(names, owner, item.name);
253
    }
254
    if let name = entry {
255
        try definition(names, owner, name);
256
    }
257
    try collectData(names, owner, &program.data[..]);
258
    unsafe {
259
        try collectFunctions(names, owner, program.fns);
260
    }
261
}