| ... | @@ -887,11 +887,13 @@ pub const DeclGen = struct { | ... | @@ -887,11 +887,13 @@ pub const DeclGen = struct { |
| 887 | .breakpoint => return, | 887 | .breakpoint => return, |
| 888 | .cond_br => return self.airCondBr(inst), | 888 | .cond_br => return self.airCondBr(inst), |
| 889 | .constant => unreachable, | 889 | .constant => unreachable, |
| | 890 | .const_ty => unreachable, |
| 890 | .dbg_stmt => return self.airDbgStmt(inst), | 891 | .dbg_stmt => return self.airDbgStmt(inst), |
| 891 | .loop => return self.airLoop(inst), | 892 | .loop => return self.airLoop(inst), |
| 892 | .ret => return self.airRet(inst), | 893 | .ret => return self.airRet(inst), |
| 893 | .ret_load => return self.airRetLoad(inst), | 894 | .ret_load => return self.airRetLoad(inst), |
| 894 | .store => return self.airStore(inst), | 895 | .store => return self.airStore(inst), |
| | 896 | .switch_br => return self.airSwitchBr(inst), |
| 895 | .unreach => return self.airUnreach(), | 897 | .unreach => return self.airUnreach(), |
| 896 | | 898 | |
| 897 | .assembly => try self.airAssembly(inst), | 899 | .assembly => try self.airAssembly(inst), |
| ... | @@ -1679,6 +1681,121 @@ pub const DeclGen = struct { | ... | @@ -1679,6 +1681,121 @@ pub const DeclGen = struct { |
| 1679 | }); | 1681 | }); |
| 1680 | } | 1682 | } |
| 1681 | | 1683 | |
| | 1684 | fn airSwitchBr(self: *DeclGen, inst: Air.Inst.Index) !void { |
| | 1685 | const target = self.getTarget(); |
| | 1686 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| | 1687 | const cond = try self.resolve(pl_op.operand); |
| | 1688 | const cond_ty = self.air.typeOf(pl_op.operand); |
| | 1689 | const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload); |
| | 1690 | |
| | 1691 | const cond_words: u32 = switch (cond_ty.zigTypeTag()) { |
| | 1692 | .Int => blk: { |
| | 1693 | const bits = cond_ty.intInfo(target).bits; |
| | 1694 | const backing_bits = self.backingIntBits(bits) orelse { |
| | 1695 | return self.todo("implement composite int switch", .{}); |
| | 1696 | }; |
| | 1697 | break :blk if (backing_bits <= 32) 1 else 2; |
| | 1698 | }, |
| | 1699 | .Enum => blk: { |
| | 1700 | var buffer: Type.Payload.Bits = undefined; |
| | 1701 | const int_ty = cond_ty.intTagType(&buffer); |
| | 1702 | const int_info = int_ty.intInfo(target); |
| | 1703 | const backing_bits = self.backingIntBits(int_info.bits) orelse { |
| | 1704 | return self.todo("implement composite int switch", .{}); |
| | 1705 | }; |
| | 1706 | break :blk if (backing_bits <= 32) 1 else 2; |
| | 1707 | }, |
| | 1708 | else => return self.todo("implement switch for type {s}", .{@tagName(cond_ty.zigTypeTag())}), // TODO: Figure out which types apply here, and work around them as we can only do integers. |
| | 1709 | }; |
| | 1710 | |
| | 1711 | const num_cases = switch_br.data.cases_len; |
| | 1712 | |
| | 1713 | // Compute the total number of arms that we need. |
| | 1714 | // Zig switches are grouped by condition, so we need to loop through all of them |
| | 1715 | const num_conditions = blk: { |
| | 1716 | var extra_index: usize = switch_br.end; |
| | 1717 | var case_i: u32 = 0; |
| | 1718 | var num_conditions: u32 = 0; |
| | 1719 | while (case_i < num_cases) : (case_i += 1) { |
| | 1720 | const case = self.air.extraData(Air.SwitchBr.Case, extra_index); |
| | 1721 | const case_body = self.air.extra[case.end + case.data.items_len ..][0..case.data.body_len]; |
| | 1722 | extra_index = case.end + case.data.items_len + case_body.len; |
| | 1723 | num_conditions += case.data.items_len; |
| | 1724 | } |
| | 1725 | break :blk num_conditions; |
| | 1726 | }; |
| | 1727 | |
| | 1728 | // First, pre-allocate the labels for the cases. |
| | 1729 | const first_case_label = self.spv.allocIds(num_cases); |
| | 1730 | // We always need the default case - if zig has none, we will generate unreachable there. |
| | 1731 | const default = self.spv.allocId(); |
| | 1732 | |
| | 1733 | // Emit the instruction before generating the blocks. |
| | 1734 | try self.func.body.emitRaw(self.spv.gpa, .OpSwitch, 2 + (cond_words + 1) * num_conditions); |
| | 1735 | self.func.body.writeOperand(IdRef, cond); |
| | 1736 | self.func.body.writeOperand(IdRef, default.toRef()); |
| | 1737 | |
| | 1738 | // Emit each of the cases |
| | 1739 | { |
| | 1740 | var extra_index: usize = switch_br.end; |
| | 1741 | var case_i: u32 = 0; |
| | 1742 | while (case_i < num_cases) : (case_i += 1) { |
| | 1743 | // SPIR-V needs a literal here, which' width depends on the case condition. |
| | 1744 | const case = self.air.extraData(Air.SwitchBr.Case, extra_index); |
| | 1745 | const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); |
| | 1746 | const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; |
| | 1747 | extra_index = case.end + case.data.items_len + case_body.len; |
| | 1748 | |
| | 1749 | const label = IdRef{ .id = first_case_label.id + case_i }; |
| | 1750 | |
| | 1751 | for (items) |item| { |
| | 1752 | const value = self.air.value(item) orelse { |
| | 1753 | return self.todo("switch on runtime value???", .{}); |
| | 1754 | }; |
| | 1755 | const int_val = switch (cond_ty.zigTypeTag()) { |
| | 1756 | .Int => if (cond_ty.isSignedInt()) @bitCast(u64, value.toSignedInt()) else value.toUnsignedInt(target), |
| | 1757 | .Enum => blk: { |
| | 1758 | var int_buffer: Value.Payload.U64 = undefined; |
| | 1759 | // TODO: figure out of cond_ty is correct (something with enum literals) |
| | 1760 | break :blk value.enumToInt(cond_ty, &int_buffer).toUnsignedInt(target); // TODO: composite integer constants |
| | 1761 | }, |
| | 1762 | else => unreachable, |
| | 1763 | }; |
| | 1764 | const int_lit: spec.LiteralContextDependentNumber = switch (cond_words) { |
| | 1765 | 1 => .{ .uint32 = @intCast(u32, int_val) }, |
| | 1766 | 2 => .{ .uint64 = int_val }, |
| | 1767 | else => unreachable, |
| | 1768 | }; |
| | 1769 | self.func.body.writeOperand(spec.LiteralContextDependentNumber, int_lit); |
| | 1770 | self.func.body.writeOperand(IdRef, label); |
| | 1771 | } |
| | 1772 | } |
| | 1773 | } |
| | 1774 | |
| | 1775 | // Now, finally, we can start emitting each of the cases. |
| | 1776 | var extra_index: usize = switch_br.end; |
| | 1777 | var case_i: u32 = 0; |
| | 1778 | while (case_i < num_cases) : (case_i += 1) { |
| | 1779 | const case = self.air.extraData(Air.SwitchBr.Case, extra_index); |
| | 1780 | const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); |
| | 1781 | const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; |
| | 1782 | extra_index = case.end + case.data.items_len + case_body.len; |
| | 1783 | |
| | 1784 | const label = IdResult{ .id = first_case_label.id + case_i }; |
| | 1785 | |
| | 1786 | try self.beginSpvBlock(label); |
| | 1787 | try self.genBody(case_body); |
| | 1788 | } |
| | 1789 | |
| | 1790 | const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; |
| | 1791 | try self.beginSpvBlock(default); |
| | 1792 | if (else_body.len != 0) { |
| | 1793 | try self.genBody(else_body); |
| | 1794 | } else { |
| | 1795 | try self.func.body.emit(self.spv.gpa, .OpUnreachable, {}); |
| | 1796 | } |
| | 1797 | } |
| | 1798 | |
| 1682 | fn airUnreach(self: *DeclGen) !void { | 1799 | fn airUnreach(self: *DeclGen) !void { |
| 1683 | try self.func.body.emit(self.spv.gpa, .OpUnreachable, {}); | 1800 | try self.func.body.emit(self.spv.gpa, .OpUnreachable, {}); |
| 1684 | } | 1801 | } |