compiler: Use checked borrows and initialized helper storage

d98e577a61408621db57dfc239c8e72aa997bca49e932df18d0c52df65657806
Alexis Sellier committed ago 1 parent 52fcce36
lib/std/lang/gen/regalloc/spill.rad +5 -5
225 225
        bitset::clear(source, c.entries[i].reg);
226 226
    }
227 227
}
228 228
229 229
/// Collect all values from a bitset into a candidates buffer with their costs.
230 -
unsafe fn collectCandidates(bs: &[u32], costs: &[SpillCost]) -> Candidates throws (alloc::AllocError) {
231 -
    let mut c = Candidates { entries: undefined, n: 0 };
230 +
fn collectCandidates(bs: &[u32], costs: &[SpillCost]) -> Candidates throws (alloc::AllocError) {
231 +
    let mut c = Candidates { entries: [CostEntry { reg: 0, cost: 0 }; MAX_CANDIDATES], n: 0 };
232 232
    let mut it = bitset::iter(bs);
233 233
    while let reg = bitset::iterNext(&mut it, bs) {
234 234
        if c.n == MAX_CANDIDATES {
235 235
            throw alloc::AllocError::OutOfMemory;
236 236
        }
241 241
    }
242 242
    return c;
243 243
}
244 244
245 245
/// Limit register pressure by marking low-cost values as spilled.
246 -
unsafe fn limitPressure(
246 +
fn limitPressure(
247 247
    live: &mut [u32],
248 248
    spilled: &mut [u32],
249 249
    costs: &[SpillCost],
250 250
    numRegs: u32
251 251
) throws (alloc::AllocError) {
268 268
/// Limit cross-call pressure by spilling values that exceed callee-saved capacity.
269 269
///
270 270
/// At a call site, every live value must survive the call in a callee-saved
271 271
/// register. If the count exceeds `numCalleeSaved`, spill the cheapest
272 272
/// crossing values.
273 -
unsafe fn limitCrossCallPressure(
273 +
fn limitCrossCallPressure(
274 274
    live: &mut [u32],
275 275
    spilled: &mut [u32],
276 276
    costs: &[SpillCost],
277 277
    calleeClass: &mut [u32],
278 278
    numCalleeSaved: u32,
279 279
    callDst: ?il::Reg
280 280
) throws (alloc::AllocError) {
281 281
    // Collect crossing candidates: live values excluding the call destination.
282 -
    let mut candidates: [CostEntry; MAX_CANDIDATES] = undefined;
282 +
    let mut candidates: [CostEntry; MAX_CANDIDATES] = [CostEntry { reg: 0, cost: 0 }; MAX_CANDIDATES];
283 283
    let mut numCandidates: u32 = 0;
284 284
    let mut it = bitset::iter(live);
285 285
    while let n = bitset::iterNext(&mut it, live) {
286 286
        if not isCallDst(callDst, n) and n < costs.len {
287 287
            if numCandidates == MAX_CANDIDATES {
lib/std/lang/il.rad +3 -3
471 471
    /// Whether the instruction has variable argument groups left to scan.
472 472
    arguments: bool,
473 473
}
474 474
475 475
/// Start a source-register scan in instruction operand order.
476 -
export unsafe fn registers(instr: &Instr) -> RegCursor {
477 -
    let mut cursor = RegCursor { fixed: undefined, count: 0, next: 0, argument: 0, branch: 0, arguments: false };
476 +
export fn registers(instr: &Instr) -> RegCursor {
477 +
    let mut cursor = RegCursor { fixed: [Reg { n: 0 }; 5], count: 0, next: 0, argument: 0, branch: 0, arguments: false };
478 478
    match *instr {
479 479
        case Instr::Reserve { size, .. } => addOperand(&mut cursor, size),
480 480
        case Instr::Load { src, .. } => addOperand(&mut cursor, Val::Reg(src)),
481 481
        case Instr::Sload { src, .. } => addOperand(&mut cursor, Val::Reg(src)),
482 482
        case Instr::Store { src, dst, .. } => {
600 600
    set cursor.arguments = false;
601 601
    return nil;
602 602
}
603 603
604 604
/// Scan an argument group from the cursor's current position.
605 -
unsafe fn nextArgument(cursor: &mut RegCursor, args: *unsafe [Val]) -> ?Reg {
605 +
fn nextArgument(cursor: &mut RegCursor, args: &[Val]) -> ?Reg {
606 606
    while cursor.argument < args.len {
607 607
        let value = args[cursor.argument];
608 608
        set cursor.argument += 1;
609 609
        if let case Val::Reg(reg) = value {
610 610
            return reg;
lib/std/lang/il/tests.rad +23 -0
1 1
//! Tests for RIL source register iteration.
2 2
3 3
use std::testing;
4 4
5 +
/// Fixed register cursors can be initialized in safe code.
6 +
@test fn testSafeRegisterCursor() throws (testing::TestError) {
7 +
    let instr = super::Instr::Ecall {
8 +
        dst: super::Reg { n: 0 },
9 +
        num: super::Val::Reg(super::Reg { n: 1 }),
10 +
        a0: super::Val::Reg(super::Reg { n: 2 }),
11 +
        a1: super::Val::Reg(super::Reg { n: 3 }),
12 +
        a2: super::Val::Reg(super::Reg { n: 4 }),
13 +
        a3: super::Val::Reg(super::Reg { n: 5 }),
14 +
    };
15 +
    let cursor = super::registers(&instr);
16 +
    try testing::expect(cursor.count == 5);
17 +
    for reg, i in &cursor.fixed[..] {
18 +
        try testing::expect(reg.n == i + 1);
19 +
    }
20 +
    let emptyInstr = super::Instr::Unreachable;
21 +
    let empty = super::registers(&emptyInstr);
22 +
    try testing::expect(empty.count == 0);
23 +
    for reg in &empty.fixed[..] {
24 +
        try testing::expect(reg.n == 0);
25 +
    }
26 +
}
27 +
5 28
/// Require exact source-register order and stable exhaustion.
6 29
unsafe fn check(instr: super::Instr, expected: &[u32]) throws (testing::TestError) {
7 30
    let mut cursor = super::registers(&instr);
8 31
    for value in expected {
9 32
        let reg = super::nextReg(&mut cursor, &instr) else panic;
lib/std/lang/lower.rad +4 -4
961 961
    return nil;
962 962
}
963 963
964 964
/// Set the package context for lowering.
965 965
/// Called before lowering each package.
966 -
export unsafe fn setPackage 'arena 'phase (self: &mut Lowerer 'arena 'phase, pkgName: *[u8]) where 'arena: 'phase {
966 +
export fn setPackage 'arena 'phase (self: &mut Lowerer 'arena 'phase, pkgName: *[u8]) where 'arena: 'phase {
967 967
    set self.pkgName = pkgName;
968 968
    set self.currentMod = nil;
969 969
    set self.packageDataStart = self.data.len;
970 970
}
971 971
2034 2034
    set self.regCounter += 1;
2035 2035
    return reg;
2036 2036
}
2037 2037
2038 2038
/// Look up the resolved type of an AST node, or throw `MissingType`.
2039 -
unsafe fn typeOf 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) -> resolver::Type throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2039 +
fn typeOf 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) -> resolver::Type throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2040 2040
    let ty = resolver::typeFor(self.low.resolver, node)
2041 2041
        else throw LowerError::MissingType(node);
2042 2042
    return ty;
2043 2043
}
2044 2044
2045 2045
/// Look up the symbol for an AST node, or throw `MissingSymbol`.
2046 -
unsafe fn symOf 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) -> *unsafe mut resolver::Symbol throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2046 +
fn symOf 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node) -> *unsafe mut resolver::Symbol throws (LowerError) where 'arena: 'phase, 'phase: 'function {
2047 2047
    let sym = resolver::nodeData(self.low.resolver, node).sym
2048 2048
        else throw LowerError::MissingSymbol(node);
2049 2049
    return sym;
2050 2050
}
2051 2051
2094 2094
        }
2095 2095
    }
2096 2096
}
2097 2097
2098 2098
/// Replace all occurrences of `from` with `to` in an args slice.
2099 -
unsafe fn rewriteValInSlice(args: *unsafe mut [il::Val], from: il::Val, to: il::Val) {
2099 +
fn rewriteValInSlice(args: &mut [il::Val], from: il::Val, to: il::Val) {
2100 2100
    for i in 0..args.len {
2101 2101
        if args[i] == from {
2102 2102
            set args[i] = to;
2103 2103
        }
2104 2104
    }
lib/std/lang/resolver.rad +12 -12
1192 1192
    set *layout = value;
1193 1193
    return layout;
1194 1194
}
1195 1195
1196 1196
/// Get the exact arguments of an applied nominal descriptor.
1197 -
export unsafe fn nominalApplication(info: *unsafe NominalType) -> ?*unsafe NominalApplication {
1197 +
export fn nominalApplication(info: &NominalType) -> ?*unsafe NominalApplication {
1198 1198
    match *info {
1199 1199
        case NominalType::Application(applied) => return applied,
1200 1200
        case NominalType::Record(body) => return body.application,
1201 1201
        case NominalType::Union(body) => return body.application,
1202 1202
        else => return nil,
1559 1559
1560 1560
    return ResolveError::Failure;
1561 1561
}
1562 1562
1563 1563
/// Like [`emitError`], but for type mismatches specifically.
1564 -
unsafe fn emitTypeMismatch 'arena (self: &mut Resolver 'arena, node: ?*ast::Node, mismatch: TypeMismatch) -> ResolveError {
1564 +
fn emitTypeMismatch 'arena (self: &mut Resolver 'arena, node: ?*ast::Node, mismatch: TypeMismatch) -> ResolveError {
1565 1565
    return emitError(self, node, ErrorKind::TypeMismatch(mismatch));
1566 1566
}
1567 1567
1568 1568
/// Allocate a scope object with the given symbol capacity.
1569 1569
unsafe fn allocScope 'arena (self: &mut Resolver 'arena, owner: *ast::Node, capacity: u32) -> *unsafe mut Scope {
1659 1659
    }
1660 1660
    return Type::Never;
1661 1661
}
1662 1662
1663 1663
/// Require that loop control statements appear inside a loop.
1664 -
unsafe fn ensureInsideLoop 'arena (self: &mut Resolver 'arena, node: *ast::Node) throws (ResolveError) {
1664 +
fn ensureInsideLoop 'arena (self: &mut Resolver 'arena, node: *ast::Node) throws (ResolveError) {
1665 1665
    if self.loopDepth == 0 {
1666 1666
        throw emitError(self, node, ErrorKind::InvalidLoopControl);
1667 1667
    }
1668 1668
}
1669 1669
1701 1701
    set self.currentFnNode = nil;
1702 1702
    exitScope(self);
1703 1703
}
1704 1704
1705 1705
/// Extract the identifier text from a node.
1706 -
unsafe fn nodeName 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *[u8]
1706 +
fn nodeName 'arena (self: &mut Resolver 'arena, node: *ast::Node) -> *[u8]
1707 1707
    throws (ResolveError)
1708 1708
{
1709 1709
    let case ast::NodeValue::Ident(name) = node.value
1710 1710
        else throw emitError(self, node, ErrorKind::ExpectedIdentifier);
1711 1711
    return name;
3157 3157
    let scope = self.scope;
3158 3158
    return try bindIdent(self, name, owner, data, attrs, scope);
3159 3159
}
3160 3160
3161 3161
/// Predicate that matches any symbol.
3162 -
fn isAnySymbol(_sym: *unsafe mut Symbol) -> bool {
3162 +
fn isAnySymbol(_sym: &Symbol) -> bool {
3163 3163
    return true;
3164 3164
}
3165 3165
3166 3166
/// Predicate that matches value or constant symbols.
3167 -
unsafe fn isValueSymbol(sym: *unsafe mut Symbol) -> bool {
3167 +
fn isValueSymbol(sym: &Symbol) -> bool {
3168 3168
    if let case SymbolData::Value { .. } = sym.data {
3169 3169
        return true;
3170 3170
    }
3171 3171
    if let case SymbolData::Constant { .. } = sym.data {
3172 3172
        return true;
3173 3173
    }
3174 3174
    return false;
3175 3175
}
3176 3176
3177 3177
/// Predicate that matches type symbols.
3178 -
unsafe fn isTypeSymbol(sym: *unsafe mut Symbol) -> bool {
3178 +
fn isTypeSymbol(sym: &Symbol) -> bool {
3179 3179
    if let case SymbolData::Type(_) = sym.data {
3180 3180
        return true;
3181 3181
    }
3182 3182
    return false;
3183 3183
}
3184 3184
3185 3185
/// Find a symbol by name in a specific scope, filtered by a predicate.
3186 -
unsafe fn findInScope(scope: *unsafe Scope, name: *[u8], predicate: unsafe fn(*unsafe mut Symbol) -> bool) -> ?*unsafe mut Symbol {
3186 +
unsafe fn findInScope(scope: *unsafe Scope, name: *[u8], predicate: fn(&Symbol) -> bool) -> ?*unsafe mut Symbol {
3187 3187
    for i in 0..scope.symbolsLen {
3188 3188
        let sym = scope.symbols[i];
3189 3189
        if sym.name == name and predicate(sym) {
3190 3190
            return sym;
3191 3191
        }
3192 3192
    }
3193 3193
    return nil;
3194 3194
}
3195 3195
3196 3196
/// Find a symbol by name, traversing scopes upwards, filtered by a predicate.
3197 -
unsafe fn findInScopeRecursive(scope: *unsafe Scope, name: *[u8], predicate: unsafe fn(*unsafe mut Symbol) -> bool) -> ?*unsafe mut Symbol {
3197 +
unsafe fn findInScopeRecursive(scope: *unsafe Scope, name: *[u8], predicate: fn(&Symbol) -> bool) -> ?*unsafe mut Symbol {
3198 3198
    let mut curr = scope;
3199 3199
    loop {
3200 3200
        if let sym = findInScope(curr, name, predicate) {
3201 3201
            return sym;
3202 3202
        }
8546 8546
    }
8547 8547
    return false;
8548 8548
}
8549 8549
8550 8550
/// Return whether a typed expression accesses a whole cell payload.
8551 -
export unsafe fn isCellDeref 'arena (self: &Resolver 'arena, node: *ast::Node) -> bool {
8551 +
export fn isCellDeref 'arena (self: &Resolver 'arena, node: *ast::Node) -> bool {
8552 8552
    if let case ast::NodeValue::Deref(target) = node.value {
8553 8553
        if let ty = typeFor(self, target) {
8554 8554
            if let case Type::Cell { .. } = ty {
8555 8555
                return true;
8556 8556
            }
10353 10353
        }
10354 10354
    }
10355 10355
}
10356 10356
10357 10357
/// Return whether a parameter borrows its argument only for the call.
10358 -
unsafe fn isBorrowedReferenceParameter(ty: Type) -> bool {
10358 +
fn isBorrowedReferenceParameter(ty: Type) -> bool {
10359 10359
    if not isRefType(ty) {
10360 10360
        return false;
10361 10361
    }
10362 10362
    match ty {
10363 10363
        case Type::Cell { class, .. } => return class == types::PointerClass::Ref,
10562 10562
    try checkLinearNode(checker, &mut elseEnv, conditional.elseExpr, usage);
10563 10563
    try joinLinearBranches(checker, env, &thenEnv, &elseEnv, node);
10564 10564
}
10565 10565
10566 10566
/// Pointer patterns borrow their subject; value patterns consume it.
10567 -
unsafe fn patternSubjectUse 'arena (self: &Resolver 'arena, subject: *ast::Node) -> LinearUse {
10567 +
fn patternSubjectUse 'arena (self: &Resolver 'arena, subject: *ast::Node) -> LinearUse {
10568 10568
    if let ty = typeFor(self, subject) {
10569 10569
        if let case Type::Pointer { .. } = ty {
10570 10570
            return LinearUse::Borrow;
10571 10571
        }
10572 10572
    }