compiler: Use scoped allocator for checked function construction
ad67060625275231a9e473c297296a32e27ff8c060f71cba88c6ebc0c9034775
1 parent
3122b8f7
lib/std/lang/lower.rad
+27 -25
| 734 | 734 | record FnLowerer: 'arena + 'phase + 'function where 'arena: 'phase, 'phase: 'function { |
|
| 735 | 735 | /// Reference to the module-level lowerer. |
|
| 736 | 736 | low: &'function mut Lowerer 'arena 'phase, |
|
| 737 | 737 | /// Arena for allocations owned by this function. |
|
| 738 | 738 | arena: &'function mut alloc::Arena, |
|
| 739 | + | /// Allocator backed by the function arena for the full lowering scope. |
|
| 740 | + | allocator: alloc::Allocator, |
|
| 739 | 741 | /// Type signature of the function being lowered. |
|
| 740 | 742 | fnType: *resolver::FnType, |
|
| 741 | 743 | /// Number of SSA variable slots required for each block. |
|
| 742 | 744 | localCount: u32, |
|
| 743 | 745 | /// Function name, used as prefix for generated data symbols. |
| 1028 | 1030 | self: &'function mut Lowerer 'arena 'phase, |
|
| 1029 | 1031 | sourceOffset: u32, |
|
| 1030 | 1032 | fnType: *resolver::FnType, |
|
| 1031 | 1033 | qualName: *[u8], |
|
| 1032 | 1034 | functionArena: &'function mut alloc::Arena, |
|
| 1033 | - | variables: &'function mut [VarData] |
|
| 1035 | + | variables: &'function mut [VarData], |
|
| 1036 | + | allocator: alloc::Allocator |
|
| 1034 | 1037 | ) -> FnLowerer 'arena 'phase 'function where 'arena: 'phase, 'phase: 'function { |
|
| 1035 | 1038 | let localCount = variables.len; |
|
| 1036 | 1039 | let mut fnLow = FnLowerer 'arena 'phase 'function { |
|
| 1037 | 1040 | low: self, |
|
| 1038 | 1041 | arena: functionArena, |
|
| 1042 | + | allocator, |
|
| 1039 | 1043 | fnType, |
|
| 1040 | 1044 | localCount, |
|
| 1041 | 1045 | fnName: qualName, |
|
| 1042 | 1046 | vars: Variables 'function { items: variables, len: 0 }, |
|
| 1043 | 1047 | params: [FnParamBinding { var: Var(0), reg: il::Reg { n: 0 } }; resolver::MAX_FN_PARAMS], |
| 1114 | 1118 | ||
| 1115 | 1119 | // Register function symbol for cross-package call resolution. |
|
| 1116 | 1120 | if let sym = resolver::symbolFor(self.resolver, node) { |
|
| 1117 | 1121 | registerSymbolName(&mut self.symbolNames, sym, qualName, alloc::arenaAllocator(self.arena)); |
|
| 1118 | 1122 | } |
|
| 1119 | - | let variableSlots = variableStorage(data.localCount, alloc::arenaAllocator(functionArena)); |
|
| 1123 | + | let allocator = alloc::arenaAllocator(functionArena); |
|
| 1124 | + | let variableSlots = variableStorage(data.localCount, allocator); |
|
| 1120 | 1125 | let parent: 'function = &mut *self, arena = &mut *functionArena, variables = &mut variableSlots[..] where 'phase: 'function in { |
|
| 1121 | - | let mut fnLow = fnLowerer(parent, node.span.offset, fnType, qualName, arena, variables); |
|
| 1126 | + | let mut fnLow = fnLowerer(parent, node.span.offset, fnType, qualName, arena, variables, allocator); |
|
| 1122 | 1127 | ||
| 1123 | 1128 | // If the function returns an aggregate or is throwing, prepend a hidden |
|
| 1124 | 1129 | // return parameter. The caller allocates the buffer and passes it |
|
| 1125 | 1130 | // as the first argument; the callee writes the return value into it. |
|
| 1126 | 1131 | if requiresReturnParam(fnType) and not isExtern { |
| 1278 | 1283 | throw LowerError::ExpectedFunction; |
|
| 1279 | 1284 | }; |
|
| 1280 | 1285 | let sym = resolver::symbolFor(self.resolver, node) else throw LowerError::MissingSymbol(node); |
|
| 1281 | 1286 | registerSymbolName(&mut self.symbolNames, sym, qualName, alloc::arenaAllocator(self.arena)); |
|
| 1282 | 1287 | ||
| 1283 | - | let variableSlots = variableStorage(data.localCount, alloc::arenaAllocator(functionArena)); |
|
| 1288 | + | let allocator = alloc::arenaAllocator(functionArena); |
|
| 1289 | + | let variableSlots = variableStorage(data.localCount, allocator); |
|
| 1284 | 1290 | let parent: 'function = &mut *self, arena = &mut *functionArena, variables = &mut variableSlots[..] where 'phase: 'function in { |
|
| 1285 | - | let mut fnLow = fnLowerer(parent, node.span.offset, fnType, qualName, arena, variables); |
|
| 1291 | + | let mut fnLow = fnLowerer(parent, node.span.offset, fnType, qualName, arena, variables, allocator); |
|
| 1286 | 1292 | if requiresReturnParam(fnType) { |
|
| 1287 | 1293 | set fnLow.returnReg = nextReg(&mut fnLow); |
|
| 1288 | 1294 | } |
|
| 1289 | 1295 | let lowParams = try lowerParams(&mut fnLow, *fnType, sig.params, receiverName); |
|
| 1290 | 1296 | let func = try! alloc::alloc(&mut *fnLow.arena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *mut il::Fn; |
| 1351 | 1357 | } |
|
| 1352 | 1358 | return buf; |
|
| 1353 | 1359 | } |
|
| 1354 | 1360 | ||
| 1355 | 1361 | /// Generate a unique label by appending the global counter to the base. |
|
| 1356 | - | unsafe fn nextLabel 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: *[u8]) -> *[u8] throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 1362 | + | fn nextLabel 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: *[u8]) -> *[u8] throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 1357 | 1363 | let idx = self.labelCounter; |
|
| 1358 | 1364 | set self.labelCounter += 1; |
|
| 1359 | 1365 | ||
| 1360 | - | return labelWithSuffix(base, idx, alloc::arenaAllocator(self.arena)); |
|
| 1366 | + | return labelWithSuffix(base, idx, self.allocator); |
|
| 1361 | 1367 | } |
|
| 1362 | 1368 | ||
| 1363 | 1369 | /////////////////////////////// |
|
| 1364 | 1370 | // Data Section Construction // |
|
| 1365 | 1371 | /////////////////////////////// |
| 2161 | 2167 | /// Create a new basic block with the given label base. |
|
| 2162 | 2168 | /// |
|
| 2163 | 2169 | /// The block is initially unsealed (predecessors may be added later) and empty. |
|
| 2164 | 2170 | /// Returns a [`BlockId`] that can be used for jumps and branches. The block must |
|
| 2165 | 2171 | /// be switched to via [`switchToBlock`] before instructions can be emitted. |
|
| 2166 | - | unsafe fn createBlock 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, labelBase: *[u8]) -> BlockId throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2172 | + | fn createBlock 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, labelBase: *[u8]) -> BlockId throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2167 | 2173 | let label = try nextLabel(self, labelBase); |
|
| 2168 | 2174 | let varCount = self.localCount; |
|
| 2169 | - | let allocator = alloc::arenaAllocator(self.arena); |
|
| 2175 | + | let allocator = self.allocator; |
|
| 2170 | 2176 | let depth = self.loopDepth; |
|
| 2171 | 2177 | return appendBlock(&mut self.blockData, label, varCount, depth, allocator); |
|
| 2172 | 2178 | } |
|
| 2173 | 2179 | ||
| 2174 | 2180 | /// Append a complete unsealed block with initialized variable slots. |
| 2198 | 2204 | ||
| 2199 | 2205 | return id; |
|
| 2200 | 2206 | } |
|
| 2201 | 2207 | ||
| 2202 | 2208 | /// Create a new block with a single parameter. |
|
| 2203 | - | unsafe fn createBlockWithParam 'arena 'phase 'function ( |
|
| 2209 | + | fn createBlockWithParam 'arena 'phase 'function ( |
|
| 2204 | 2210 | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 2205 | 2211 | labelBase: *[u8], |
|
| 2206 | 2212 | param: il::Param |
|
| 2207 | 2213 | ) -> BlockId throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2208 | 2214 | let block = try createBlock(self, labelBase); |
|
| 2209 | - | let allocator = alloc::arenaAllocator(self.arena); |
|
| 2215 | + | let allocator = self.allocator; |
|
| 2210 | 2216 | self.blockData[*block].params.append(param, allocator); |
|
| 2211 | 2217 | ||
| 2212 | 2218 | return block; |
|
| 2213 | 2219 | } |
|
| 2214 | 2220 |
| 2266 | 2272 | ////////////////////////// |
|
| 2267 | 2273 | // Instruction Emission // |
|
| 2268 | 2274 | ////////////////////////// |
|
| 2269 | 2275 | ||
| 2270 | 2276 | /// Emit an instruction to the current block. |
|
| 2271 | - | unsafe fn emit 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, instr: il::Instr) where 'arena: 'phase, 'phase: 'function { |
|
| 2272 | - | let allocator = alloc::arenaAllocator(self.arena); |
|
| 2277 | + | fn emit 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, instr: il::Instr) where 'arena: 'phase, 'phase: 'function { |
|
| 2278 | + | let allocator = self.allocator; |
|
| 2273 | 2279 | recordInstruction(self, &instr, allocator); |
|
| 2274 | 2280 | } |
|
| 2275 | 2281 | ||
| 2276 | 2282 | /// Append an instruction and its enabled source location to the current block. |
|
| 2277 | 2283 | fn recordInstruction 'arena 'phase 'function ( |
| 2772 | 2778 | // Control Flow Edge Management // |
|
| 2773 | 2779 | ////////////////////////////////// |
|
| 2774 | 2780 | ||
| 2775 | 2781 | /// Add a predecessor edge from `pred` to `target`. |
|
| 2776 | 2782 | /// Must be called before the target block is sealed. Duplicates are ignored. |
|
| 2777 | - | unsafe fn addPredecessor 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: BlockId, pred: BlockId) where 'arena: 'phase, 'phase: 'function { |
|
| 2778 | - | let allocator = alloc::arenaAllocator(self.arena); |
|
| 2783 | + | fn addPredecessor 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: BlockId, pred: BlockId) where 'arena: 'phase, 'phase: 'function { |
|
| 2784 | + | let allocator = self.allocator; |
|
| 2779 | 2785 | insertPredecessor(&mut self.blockData[..], target, pred, allocator); |
|
| 2780 | 2786 | } |
|
| 2781 | 2787 | ||
| 2782 | 2788 | /// Insert one distinct predecessor into an unsealed block's owned list. |
|
| 2783 | 2789 | fn insertPredecessor(blocks: &mut [BlockData], target: BlockId, pred: BlockId, allocator: alloc::Allocator) { |
| 2840 | 2846 | } |
|
| 2841 | 2847 | return self.loopStack[self.loopDepth - 1]; |
|
| 2842 | 2848 | } |
|
| 2843 | 2849 | ||
| 2844 | 2850 | /// Get or lazily create the continue target block for the current loop. |
|
| 2845 | - | unsafe fn getOrCreateContinueBlock 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function) -> BlockId throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2851 | + | fn getOrCreateContinueBlock 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function) -> BlockId throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2846 | 2852 | let ctx = currentLoop(self) else { |
|
| 2847 | 2853 | throw LowerError::OutsideOfLoop; |
|
| 2848 | 2854 | }; |
|
| 2849 | 2855 | if let block = ctx.continueTarget { |
|
| 2850 | 2856 | return block; |
| 3018 | 3024 | let val = try useVarInBlock(self, pred, v); |
|
| 3019 | 3025 | set self.blockData[*block].vars[*v] = val; // Cache. |
|
| 3020 | 3026 | return val; |
|
| 3021 | 3027 | } |
|
| 3022 | 3028 | case VarSource::Merge => { |
|
| 3023 | - | unsafe { |
|
| 3024 | - | let allocator = alloc::arenaAllocator(self.arena); |
|
| 3025 | - | return try createBlockParam(self, block, v, allocator); |
|
| 3026 | - | } |
|
| 3029 | + | let allocator = self.allocator; |
|
| 3030 | + | return try createBlockParam(self, block, v, allocator); |
|
| 3027 | 3031 | }, |
|
| 3028 | 3032 | case VarSource::Invalid => throw LowerError::InvalidUse, |
|
| 3029 | 3033 | } |
|
| 3030 | 3034 | } |
|
| 3031 | 3035 |
| 3182 | 3186 | let pred = BlockId(self.blockData[*block].preds[index]); |
|
| 3183 | 3187 | // This may recursively trigger more block arg resolution if the |
|
| 3184 | 3188 | // predecessor also needs to look up the variable from its predecessors. |
|
| 3185 | 3189 | let val = try useVarInBlock(self, pred, v); |
|
| 3186 | 3190 | assert val <> il::Val::Undef, "createBlockParam: predecessor provides undef value for block parameter"; |
|
| 3187 | - | unsafe { |
|
| 3188 | - | patchTerminatorArg(self, pred, *block, paramIdx, val); |
|
| 3189 | - | } |
|
| 3191 | + | patchTerminatorArg(self, pred, *block, paramIdx, val); |
|
| 3190 | 3192 | } |
|
| 3191 | 3193 | } |
|
| 3192 | 3194 | ||
| 3193 | 3195 | /// Check if a block parameter is trivial, i.e. all predecessors provide |
|
| 3194 | 3196 | /// the same value. Returns the trivial value if so. |
| 3224 | 3226 | return sameVal; |
|
| 3225 | 3227 | } |
|
| 3226 | 3228 | ||
| 3227 | 3229 | /// Patch a single terminator argument for a specific edge. This is used during |
|
| 3228 | 3230 | /// SSA construction to pass variable values along control flow edges. |
|
| 3229 | - | unsafe fn patchTerminatorArg 'arena 'phase 'function ( |
|
| 3231 | + | fn patchTerminatorArg 'arena 'phase 'function ( |
|
| 3230 | 3232 | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 3231 | 3233 | from: BlockId, // The predecessor block containing the terminator to patch. |
|
| 3232 | 3234 | target: u32, // The index of the target block we're passing the value to. |
|
| 3233 | 3235 | paramIdx: u32, // The index of the block parameter to set. |
|
| 3234 | 3236 | val: il::Val // The value to pass as the argument. |
|
| 3235 | 3237 | ) where 'arena: 'phase, 'phase: 'function { |
|
| 3236 | - | let allocator = alloc::arenaAllocator(self.arena); |
|
| 3238 | + | let allocator = self.allocator; |
|
| 3237 | 3239 | // Get mutable block data by block id. |
|
| 3238 | 3240 | let data = &mut self.blockData[*from]; |
|
| 3239 | 3241 | let ix = data.instrs.len - 1; // The terminator is always the last instruction. |
|
| 3240 | 3242 | patchEdgeArgs(&mut data.instrs[ix], target, paramIdx, val, allocator); |
|
| 3241 | 3243 | } |