compiler: Use checked borrows and initialized helper storage
d98e577a61408621db57dfc239c8e72aa997bca49e932df18d0c52df65657806
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 | } |