compiler: Own switch tables and check case insertion
a14104e22b731ed92d00e1f878c117832366397bb4afddf0a5355fc572dc236a
1 parent
b05a8d87
lib/std/lang/lower.rad
+23 -27
| 3756 | 3756 | } |
|
| 3757 | 3757 | } |
|
| 3758 | 3758 | return try finalizeBlocks(self); |
|
| 3759 | 3759 | } |
|
| 3760 | 3760 | ||
| 3761 | + | /// Append an initialized switch case to owned storage. |
|
| 3762 | + | fn appendSwitchCase( |
|
| 3763 | + | cases: &mut *mut [il::SwitchCase], |
|
| 3764 | + | value: i64, |
|
| 3765 | + | target: BlockId, |
|
| 3766 | + | args: *unsafe mut [il::Val], |
|
| 3767 | + | allocator: alloc::Allocator, |
|
| 3768 | + | ) { |
|
| 3769 | + | cases.append(il::SwitchCase { value, target: *target, args }, allocator); |
|
| 3770 | + | } |
|
| 3771 | + | ||
| 3761 | 3772 | /// Lower a scalar match as a switch instruction. |
|
| 3762 | 3773 | unsafe fn lowerMatchSwitch 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, prongs: *[*ast::Node], subject: &MatchSubject, mergeBlock: &mut ?BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3763 | 3774 | let mut blocks: *mut [BlockId] = &mut []; |
|
| 3764 | - | let mut cases: *unsafe mut [il::SwitchCase] = &mut []; |
|
| 3775 | + | let mut cases: *mut [il::SwitchCase] = &mut []; |
|
| 3765 | 3776 | let mut defaultIdx: u32 = 0; |
|
| 3766 | 3777 | let entry = currentBlock(self); |
|
| 3767 | 3778 | ||
| 3768 | 3779 | for p, i in prongs { |
|
| 3769 | 3780 | let case ast::NodeValue::MatchProng(prong) = p.value |
| 3778 | 3789 | blocks.append(try createBlock(self, "case"), alloc::arenaAllocator(self.arena)); |
|
| 3779 | 3790 | for pat in pats { |
|
| 3780 | 3791 | let cv = resolver::constValueEntry(self.low.resolver, pat) |
|
| 3781 | 3792 | else throw LowerError::MissingConst(pat); |
|
| 3782 | 3793 | ||
| 3783 | - | cases.append(il::SwitchCase { |
|
| 3784 | - | value: constToScalar(cv), |
|
| 3785 | - | target: *blocks[i], |
|
| 3786 | - | args: &mut [] |
|
| 3787 | - | }, alloc::arenaAllocator(self.arena)); |
|
| 3794 | + | appendSwitchCase(&mut cases, constToScalar(cv), blocks[i], &mut [], alloc::arenaAllocator(self.arena)); |
|
| 3788 | 3795 | } |
|
| 3789 | 3796 | } |
|
| 3790 | 3797 | } |
|
| 3791 | 3798 | addPredecessor(self, blocks[i], entry); |
|
| 3792 | 3799 | } |
|
| 3793 | 3800 | emit(self, il::Instr::Switch { |
|
| 3794 | 3801 | val: subject.val, |
|
| 3795 | 3802 | defaultTarget: *blocks[defaultIdx], |
|
| 3796 | 3803 | defaultArgs: &mut [], |
|
| 3797 | - | cases: &mut cases[..] |
|
| 3804 | + | cases: (&mut cases[..]) as *unsafe mut [il::SwitchCase] |
|
| 3798 | 3805 | }); |
|
| 3799 | 3806 | ||
| 3800 | 3807 | for p, i in prongs { |
|
| 3801 | 3808 | let case ast::NodeValue::MatchProng(prong) = p.value |
|
| 3802 | 3809 | else throw LowerError::UnexpectedNodeValue(p); |
| 4850 | 4857 | addPredecessor(self, mergeBlock, currentBlock(self)); |
|
| 4851 | 4858 | ||
| 4852 | 4859 | // Create comparison blocks for each non-void variant and build switch cases. |
|
| 4853 | 4860 | // Void variants jump directly to merge with `true`. |
|
| 4854 | 4861 | let trueArgs = try allocVal(self, il::Val::Imm(1)); |
|
| 4855 | - | let mut cases: *unsafe mut [il::SwitchCase] = &mut []; |
|
| 4862 | + | let mut cases: *mut [il::SwitchCase] = &mut []; |
|
| 4856 | 4863 | for variant, i in unionInfo.variants { |
|
| 4857 | 4864 | if variant.valueType == resolver::Type::Void { |
|
| 4858 | - | cases.append(il::SwitchCase { |
|
| 4859 | - | value: i as i64, |
|
| 4860 | - | target: *mergeBlock, |
|
| 4861 | - | args: trueArgs |
|
| 4862 | - | }, alloc::arenaAllocator(self.arena)); |
|
| 4865 | + | appendSwitchCase(&mut cases, i as i64, mergeBlock, trueArgs, alloc::arenaAllocator(self.arena)); |
|
| 4863 | 4866 | } else { |
|
| 4864 | 4867 | let payloadBlock = try createBlock(self, "eq#payload"); |
|
| 4865 | - | cases.append(il::SwitchCase { |
|
| 4866 | - | value: i as i64, |
|
| 4867 | - | target: *payloadBlock, |
|
| 4868 | - | args: &mut [] |
|
| 4869 | - | }, alloc::arenaAllocator(self.arena)); |
|
| 4868 | + | appendSwitchCase(&mut cases, i as i64, payloadBlock, &mut [], alloc::arenaAllocator(self.arena)); |
|
| 4870 | 4869 | } |
|
| 4871 | 4870 | } |
|
| 4872 | 4871 | ||
| 4873 | 4872 | // Emit switch in @tag block. Default arm is unreachable since we cover all variants. |
|
| 4874 | 4873 | let unreachableBlock = try createBlock(self, "eq#unreachable"); |
|
| 4875 | 4874 | try switchToAndSeal(self, tagBlock); |
|
| 4876 | 4875 | emit(self, il::Instr::Switch { |
|
| 4877 | 4876 | val: tagA, |
|
| 4878 | 4877 | defaultTarget: *unreachableBlock, |
|
| 4879 | 4878 | defaultArgs: &mut [], |
|
| 4880 | - | cases |
|
| 4879 | + | cases: (&mut cases[..]) as *unsafe mut [il::SwitchCase] |
|
| 4881 | 4880 | }); |
|
| 4882 | 4881 | ||
| 4883 | 4882 | // Add predecessor edges for switch targets. |
|
| 4884 | 4883 | addPredecessor(self, unreachableBlock, tagBlock); |
|
| 4885 | - | for c in cases { |
|
| 4884 | + | for c in &cases[..] { |
|
| 4886 | 4885 | addPredecessor(self, BlockId(c.target), tagBlock); |
|
| 4887 | 4886 | } |
|
| 4888 | 4887 | let valOffset = unionInfo.valOffset as i32; |
|
| 4889 | 4888 | ||
| 4890 | 4889 | // Emit payload comparison blocks for non-void variants. |
| 6642 | 6641 | ) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6643 | 6642 | let entry = currentBlock(self); |
|
| 6644 | 6643 | ||
| 6645 | 6644 | // First pass: create blocks, resolve error types, and build switch cases. |
|
| 6646 | 6645 | let mut targets: [?CatchTarget; MAX_CATCH_CLAUSES] = [nil; MAX_CATCH_CLAUSES]; |
|
| 6647 | - | let mut cases: *unsafe mut [il::SwitchCase] = &mut []; |
|
| 6646 | + | let mut cases: *mut [il::SwitchCase] = &mut []; |
|
| 6648 | 6647 | let mut defaultIdx: ?u32 = nil; |
|
| 6649 | 6648 | ||
| 6650 | 6649 | for clauseNode, i in catches { |
|
| 6651 | 6650 | let case ast::NodeValue::CatchClause(clause) = clauseNode.value |
|
| 6652 | 6651 | else panic "lowerMultiCatch: expected CatchClause"; |
| 6656 | 6655 | ||
| 6657 | 6656 | if let typeNode = clause.typeNode { |
|
| 6658 | 6657 | let errTy = try typeOf(self, typeNode); |
|
| 6659 | 6658 | set targets[i] = CatchTarget { block, errorType: errTy }; |
|
| 6660 | 6659 | ||
| 6661 | - | cases.append(il::SwitchCase { |
|
| 6662 | - | value: getOrAssignErrorTag(self.low, errTy) as i64, |
|
| 6663 | - | target: *block, |
|
| 6664 | - | args: &mut [] |
|
| 6665 | - | }, alloc::arenaAllocator(self.arena)); |
|
| 6660 | + | let tag = getOrAssignErrorTag(self.low, errTy) as i64; |
|
| 6661 | + | appendSwitchCase(&mut cases, tag, block, &mut [], alloc::arenaAllocator(self.arena)); |
|
| 6666 | 6662 | } else { |
|
| 6667 | 6663 | set targets[i] = CatchTarget { block, errorType: nil }; |
|
| 6668 | 6664 | set defaultIdx = i; |
|
| 6669 | 6665 | } |
|
| 6670 | 6666 | } |
| 6681 | 6677 | let defaultTarget = selectedDefault else panic "lowerMultiCatch: missing default destination"; |
|
| 6682 | 6678 | emit(self, il::Instr::Switch { |
|
| 6683 | 6679 | val: il::Val::Reg(tagReg), |
|
| 6684 | 6680 | defaultTarget: *defaultTarget, |
|
| 6685 | 6681 | defaultArgs: &mut [], |
|
| 6686 | - | cases |
|
| 6682 | + | cases: (&mut cases[..]) as *unsafe mut [il::SwitchCase] |
|
| 6687 | 6683 | }); |
|
| 6688 | 6684 | ||
| 6689 | 6685 | // Second pass: emit each catch clause body. |
|
| 6690 | 6686 | for clauseNode, i in catches { |
|
| 6691 | 6687 | let case ast::NodeValue::CatchClause(clause) = clauseNode.value |
test/tests/match.switch.capacity.rad
+16 -0
| 147 | 147 | case 63 => return 163, |
|
| 148 | 148 | else => return 999, |
|
| 149 | 149 | } |
|
| 150 | 150 | } |
|
| 151 | 151 | ||
| 152 | + | /// Multiple case values can share a destination and its merged value. |
|
| 153 | + | fn grouped(value: u32) -> u32 { |
|
| 154 | + | let mut result: u32 = 0; |
|
| 155 | + | match value { |
|
| 156 | + | case 0, 2, 4, 6 => set result = 10, |
|
| 157 | + | case 1, 3, 5, 7 => set result = 20, |
|
| 158 | + | else => set result = 30, |
|
| 159 | + | } |
|
| 160 | + | return result + value; |
|
| 161 | + | } |
|
| 162 | + | ||
| 152 | 163 | /// Exercise every case and each fallback. |
|
| 153 | 164 | @default fn main() -> u32 { |
|
| 154 | 165 | for value in 0..31 { assert select31(value) == value + 100; } |
|
| 155 | 166 | assert select31(31) == 999; |
|
| 156 | 167 | for value in 0..32 { assert select32(value) == value + 100; } |
|
| 157 | 168 | assert select32(32) == 999; |
|
| 158 | 169 | for value in 0..64 { assert select64(value) == value + 100; } |
|
| 159 | 170 | assert select64(64) == 999; |
|
| 171 | + | for value in 0..8 { |
|
| 172 | + | let base: u32 = 10 if value % 2 == 0 else 20; |
|
| 173 | + | assert grouped(value) == base + value; |
|
| 174 | + | } |
|
| 175 | + | assert grouped(8) == 38; |
|
| 160 | 176 | return 0; |
|
| 161 | 177 | } |