compiler: Detect terminators through checked instruction slices
415fdcf8a2c227b5d20827a881fec7f205f38d8b37bbdd0636dcadc0157b94d9
1 parent
06628001
lib/std/lang/lower.rad
+15 -16
| 2665 | 2665 | } |
|
| 2666 | 2666 | } |
|
| 2667 | 2667 | ||
| 2668 | 2668 | /// Emit a jump to target if the current block hasn't terminated, then seal the target block. |
|
| 2669 | 2669 | unsafe fn emitJmpAndSeal 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2670 | - | if not blockHasTerminator(self) { |
|
| 2670 | + | if not blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) { |
|
| 2671 | 2671 | try emitJmp(self, target); |
|
| 2672 | 2672 | } |
|
| 2673 | 2673 | try sealBlock(self, target); |
|
| 2674 | 2674 | } |
|
| 2675 | 2675 | ||
| 2676 | - | /// Check if the current block already has a terminator instruction. |
|
| 2677 | - | unsafe fn blockHasTerminator 'arena 'phase 'function (self: &FnLowerer 'arena 'phase 'function) -> bool where 'arena: 'phase, 'phase: 'function { |
|
| 2678 | - | let blk = getBlock(&self.blockData[..], currentBlock(self)); |
|
| 2679 | - | if blk.instrs.len == 0 { |
|
| 2676 | + | /// Check if a block's instruction list ends with a terminator. |
|
| 2677 | + | fn blockHasTerminator(instrs: &[il::Instr]) -> bool { |
|
| 2678 | + | if instrs.len == 0 { |
|
| 2680 | 2679 | return false; |
|
| 2681 | 2680 | } |
|
| 2682 | - | match blk.instrs[blk.instrs.len - 1] { |
|
| 2681 | + | match instrs[instrs.len - 1] { |
|
| 2683 | 2682 | case il::Instr::Ret { .. }, |
|
| 2684 | 2683 | il::Instr::Jmp { .. }, |
|
| 2685 | 2684 | il::Instr::Br { .. }, |
|
| 2686 | 2685 | il::Instr::Switch { .. }, |
|
| 2687 | 2686 | il::Instr::Unreachable => |
| 2705 | 2704 | /// } |
|
| 2706 | 2705 | /// |
|
| 2707 | 2706 | /// In the above example, the merge block stays `nil`, and no code is generated |
|
| 2708 | 2707 | /// after the `if`. The merge block is created on first use. |
|
| 2709 | 2708 | unsafe fn emitMergeIfUnterminated 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, mergeBlock: &mut ?BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2710 | - | if not blockHasTerminator(self) { |
|
| 2709 | + | if not blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) { |
|
| 2711 | 2710 | if *mergeBlock == nil { |
|
| 2712 | 2711 | set *mergeBlock = try createBlock(self, "merge"); |
|
| 2713 | 2712 | } |
|
| 2714 | 2713 | let target = *mergeBlock else { throw LowerError::MissingTarget; }; |
|
| 2715 | 2714 | try emitJmp(self, target); |
| 3676 | 3675 | } |
|
| 3677 | 3676 | /// Lower function body. |
|
| 3678 | 3677 | try lowerBlock(self, body); |
|
| 3679 | 3678 | ||
| 3680 | 3679 | // Add implicit return if body doesn't diverge. |
|
| 3681 | - | if not blockHasTerminator(self) { |
|
| 3680 | + | if not blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) { |
|
| 3682 | 3681 | if *self.fnType.returnType == resolver::Type::Never { |
|
| 3683 | 3682 | emit(self, il::Instr::Unreachable); |
|
| 3684 | 3683 | } else if self.fnType.throwList.len > 0 { |
|
| 3685 | 3684 | if *self.fnType.returnType == resolver::Type::Void { |
|
| 3686 | 3685 | // Implicit `void` return in throwing function: wrap in result success. |
| 4080 | 4079 | let values = try allocVals(self, bindings.len); |
|
| 4081 | 4080 | for bindingNode, i in bindings { |
|
| 4082 | 4081 | let case ast::NodeValue::RegionBinding(binding) = bindingNode.value |
|
| 4083 | 4082 | else throw LowerError::ExpectedIdentifier; |
|
| 4084 | 4083 | set values[i] = try lowerExpr(self, binding.value); |
|
| 4085 | - | if blockHasTerminator(self) { |
|
| 4084 | + | if blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) { |
|
| 4086 | 4085 | exitVarScope(&mut self.vars, savedVarsLen); |
|
| 4087 | 4086 | return; |
|
| 4088 | 4087 | } |
|
| 4089 | 4088 | } |
|
| 4090 | 4089 | for bindingNode, i in bindings { |
| 4187 | 4186 | let savedVarsLen = enterVarScope(&self.vars); |
|
| 4188 | 4187 | for stmt in blk.statements { |
|
| 4189 | 4188 | try lowerNode(self, stmt); |
|
| 4190 | 4189 | ||
| 4191 | 4190 | // If the statement diverges, further statements are unreachable. |
|
| 4192 | - | if blockHasTerminator(self) { |
|
| 4191 | + | if blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) { |
|
| 4193 | 4192 | exitVarScope(&mut self.vars, savedVarsLen); |
|
| 4194 | 4193 | return; |
|
| 4195 | 4194 | } |
|
| 4196 | 4195 | } |
|
| 4197 | 4196 | exitVarScope(&mut self.vars, savedVarsLen); |
| 5373 | 5372 | ||
| 5374 | 5373 | /// Lower a let binding. |
|
| 5375 | 5374 | unsafe fn lowerLet 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, l: ast::Let) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5376 | 5375 | // Evaluate value. |
|
| 5377 | 5376 | let val = try lowerExpr(self, l.value); |
|
| 5378 | - | if blockHasTerminator(self) { |
|
| 5377 | + | if blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) { |
|
| 5379 | 5378 | return; |
|
| 5380 | 5379 | } |
|
| 5381 | 5380 | try bindLetValue(self, node, l, val); |
|
| 5382 | 5381 | } |
|
| 5383 | 5382 |
| 5817 | 5816 | try emitJmpAndSeal(self, stepBlock); |
|
| 5818 | 5817 | switchToBlock(self, stepBlock); |
|
| 5819 | 5818 | } |
|
| 5820 | 5819 | // Otherwise, emit increment directly in the current block, |
|
| 5821 | 5820 | // saving the jump to a separate step block. |
|
| 5822 | - | if not blockHasTerminator(self) { |
|
| 5821 | + | if not blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) { |
|
| 5823 | 5822 | match *iter { |
|
| 5824 | 5823 | case ForIter::Range { valVar, valType, indexVar, .. } => { |
|
| 5825 | 5824 | try emitIncrement(self, valVar, valType); |
|
| 5826 | 5825 | if let idxVar = indexVar { |
|
| 5827 | 5826 | try emitIncrement(self, idxVar, il::Type::W32); |
| 5958 | 5957 | unsafe fn lowerReturnStmt 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, node: *ast::Node, value: ?*ast::Node) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5959 | 5958 | let mut val = il::Val::Undef; |
|
| 5960 | 5959 | if let expr = value { |
|
| 5961 | 5960 | set val = try lowerExpr(self, expr); |
|
| 5962 | 5961 | } |
|
| 5963 | - | if blockHasTerminator(self) { |
|
| 5962 | + | if blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) { |
|
| 5964 | 5963 | return; |
|
| 5965 | 5964 | } |
|
| 5966 | 5965 | set val = try applyCoercion(self, node, val); |
|
| 5967 | 5966 | try emitRetVal(self, val); |
|
| 5968 | 5967 | } |
| 6462 | 6461 | } else if let case resolver::NodeExtra::MethodCall { method } = callNodeExtra { |
|
| 6463 | 6462 | set resVal = try lowerMethodCall(self, t.expr, callExpr, method); |
|
| 6464 | 6463 | } else { |
|
| 6465 | 6464 | set resVal = try lowerCall(self, t.expr, callExpr); |
|
| 6466 | 6465 | } |
|
| 6467 | - | if blockHasTerminator(self) { |
|
| 6466 | + | if blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) { |
|
| 6468 | 6467 | return il::Val::Undef; |
|
| 6469 | 6468 | } |
|
| 6470 | 6469 | let base = emitValToReg(self, resVal); // The result value. |
|
| 6471 | 6470 | let tagReg = resultTagReg(self, base); // The result tag. |
|
| 6472 | 6471 |
| 6890 | 6889 | emitLoadW64At( |
|
| 6891 | 6890 | self, functionReg, vtableReg, |
|
| 6892 | 6891 | (allocation.methodIndex * resolver::PTR_SIZE) as i32, |
|
| 6893 | 6892 | ); |
|
| 6894 | 6893 | let value = try lowerCallArg(self, call.args[0], true); |
|
| 6895 | - | if blockHasTerminator(self) { |
|
| 6894 | + | if blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) { |
|
| 6896 | 6895 | return SessionInitialization { allocation, result: il::Val::Undef, value, count: il::Val::Undef }; |
|
| 6897 | 6896 | } |
|
| 6898 | 6897 | let slice = allocation.kind <> resolver::SessionAllocationKind::New; |
|
| 6899 | 6898 | let mut count = il::Val::Imm(1); |
|
| 6900 | 6899 | if allocation.kind == resolver::SessionAllocationKind::Copy { |
|
| 6901 | 6900 | set count = loadSliceLen(self, emitValToReg(self, value)); |
|
| 6902 | 6901 | } else if allocation.kind == resolver::SessionAllocationKind::Fill { |
|
| 6903 | 6902 | set count = try lowerExpr(self, call.args[1]); |
|
| 6904 | - | if blockHasTerminator(self) { |
|
| 6903 | + | if blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) { |
|
| 6905 | 6904 | return SessionInitialization { allocation, result: il::Val::Undef, value, count: il::Val::Undef }; |
|
| 6906 | 6905 | } |
|
| 6907 | 6906 | } |
|
| 6908 | 6907 | let layout = resolver::getTypeLayout(*allocation.item); |
|
| 6909 | 6908 | let size = layout.size if layout.size > 0 else 1; |