| ... | @@ -30,6 +30,7 @@ const build_options = @import("build_options"); | ... | @@ -30,6 +30,7 @@ const build_options = @import("build_options"); |
| 30 | const bits = @import("bits.zig"); | 30 | const bits = @import("bits.zig"); |
| 31 | const abi = @import("abi.zig"); | 31 | const abi = @import("abi.zig"); |
| 32 | const Instruction = bits.Instruction; | 32 | const Instruction = bits.Instruction; |
| | 33 | const ShiftWidth = Instruction.ShiftWidth; |
| 33 | const Register = bits.Register; | 34 | const Register = bits.Register; |
| 34 | | 35 | |
| 35 | const Self = @This(); | 36 | const Self = @This(); |
| ... | @@ -637,7 +638,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -637,7 +638,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 637 | .ptr_slice_ptr_ptr => @panic("TODO try self.airPtrSlicePtrPtr(inst)"), | 638 | .ptr_slice_ptr_ptr => @panic("TODO try self.airPtrSlicePtrPtr(inst)"), |
| 638 | | 639 | |
| 639 | .array_elem_val => @panic("TODO try self.airArrayElemVal(inst)"), | 640 | .array_elem_val => @panic("TODO try self.airArrayElemVal(inst)"), |
| 640 | .slice_elem_val => @panic("TODO try self.airSliceElemVal(inst)"), | 641 | .slice_elem_val => try self.airSliceElemVal(inst), |
| 641 | .slice_elem_ptr => @panic("TODO try self.airSliceElemPtr(inst)"), | 642 | .slice_elem_ptr => @panic("TODO try self.airSliceElemPtr(inst)"), |
| 642 | .ptr_elem_val => @panic("TODO try self.airPtrElemVal(inst)"), | 643 | .ptr_elem_val => @panic("TODO try self.airPtrElemVal(inst)"), |
| 643 | .ptr_elem_ptr => @panic("TODO try self.airPtrElemPtr(inst)"), | 644 | .ptr_elem_ptr => @panic("TODO try self.airPtrElemPtr(inst)"), |
| ... | @@ -1374,16 +1375,48 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1374,16 +1375,48 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 1374 | return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); | 1375 | return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); |
| 1375 | } | 1376 | } |
| 1376 | | 1377 | |
| 1377 | fn airStore(self: *Self, inst: Air.Inst.Index) !void { | 1378 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| | 1379 | const is_volatile = false; // TODO |
| 1378 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1380 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1379 | const ptr = try self.resolveInst(bin_op.lhs); | | |
| 1380 | const value = try self.resolveInst(bin_op.rhs); | | |
| 1381 | const ptr_ty = self.air.typeOf(bin_op.lhs); | | |
| 1382 | const value_ty = self.air.typeOf(bin_op.rhs); | | |
| 1383 | | 1381 | |
| 1384 | try self.store(ptr, value, ptr_ty, value_ty); | 1382 | if (!is_volatile and self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| | 1383 | const result: MCValue = result: { |
| | 1384 | const slice_mcv = try self.resolveInst(bin_op.lhs); |
| | 1385 | const index_mcv = try self.resolveInst(bin_op.rhs); |
| 1385 | | 1386 | |
| 1386 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); | 1387 | const slice_ty = self.air.typeOf(bin_op.lhs); |
| | 1388 | const elem_ty = slice_ty.childType(); |
| | 1389 | const elem_size = elem_ty.abiSize(self.target.*); |
| | 1390 | |
| | 1391 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| | 1392 | const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf); |
| | 1393 | |
| | 1394 | const index_lock: ?RegisterLock = if (index_mcv == .register) |
| | 1395 | self.register_manager.lockRegAssumeUnused(index_mcv.register) |
| | 1396 | else |
| | 1397 | null; |
| | 1398 | defer if (index_lock) |reg| self.register_manager.unlockReg(reg); |
| | 1399 | |
| | 1400 | const base_mcv: MCValue = switch (slice_mcv) { |
| | 1401 | .stack_offset => |off| .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, .{ .stack_offset = off }) }, |
| | 1402 | else => return self.fail("TODO slice_elem_val when slice is {}", .{slice_mcv}), |
| | 1403 | }; |
| | 1404 | const base_lock = self.register_manager.lockRegAssumeUnused(base_mcv.register); |
| | 1405 | defer self.register_manager.unlockReg(base_lock); |
| | 1406 | |
| | 1407 | switch (elem_size) { |
| | 1408 | else => { |
| | 1409 | // TODO skip the ptr_add emission entirely and use native addressing modes |
| | 1410 | // i.e sllx/mulx then R+R or scale immediate then R+I |
| | 1411 | const dest = try self.allocRegOrMem(inst, true); |
| | 1412 | const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ptr_field_type, Type.usize); |
| | 1413 | try self.load(dest, addr, slice_ptr_field_type); |
| | 1414 | |
| | 1415 | break :result dest; |
| | 1416 | }, |
| | 1417 | } |
| | 1418 | }; |
| | 1419 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1387 | } | 1420 | } |
| 1388 | | 1421 | |
| 1389 | fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { | 1422 | fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -1407,6 +1440,18 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1407,6 +1440,18 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { |
| 1407 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 1440 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1408 | } | 1441 | } |
| 1409 | | 1442 | |
| | 1443 | fn airStore(self: *Self, inst: Air.Inst.Index) !void { |
| | 1444 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| | 1445 | const ptr = try self.resolveInst(bin_op.lhs); |
| | 1446 | const value = try self.resolveInst(bin_op.rhs); |
| | 1447 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| | 1448 | const value_ty = self.air.typeOf(bin_op.rhs); |
| | 1449 | |
| | 1450 | try self.store(ptr, value, ptr_ty, value_ty); |
| | 1451 | |
| | 1452 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| | 1453 | } |
| | 1454 | |
| 1410 | fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { | 1455 | fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 1411 | _ = self; | 1456 | _ = self; |
| 1412 | _ = inst; | 1457 | _ = inst; |
| ... | @@ -1561,10 +1606,226 @@ fn binOp( | ... | @@ -1561,10 +1606,226 @@ fn binOp( |
| 1561 | } | 1606 | } |
| 1562 | }, | 1607 | }, |
| 1563 | | 1608 | |
| | 1609 | .mul => { |
| | 1610 | switch (lhs_ty.zigTypeTag()) { |
| | 1611 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| | 1612 | .Int => { |
| | 1613 | assert(lhs_ty.eql(rhs_ty, mod)); |
| | 1614 | const int_info = lhs_ty.intInfo(self.target.*); |
| | 1615 | if (int_info.bits <= 64) { |
| | 1616 | // If LHS is immediate, then swap it with RHS. |
| | 1617 | const lhs_is_imm = lhs == .immediate; |
| | 1618 | const new_lhs = if (lhs_is_imm) rhs else lhs; |
| | 1619 | const new_rhs = if (lhs_is_imm) lhs else rhs; |
| | 1620 | const new_lhs_ty = if (lhs_is_imm) rhs_ty else lhs_ty; |
| | 1621 | const new_rhs_ty = if (lhs_is_imm) lhs_ty else rhs_ty; |
| | 1622 | |
| | 1623 | // At this point, RHS might be an immediate |
| | 1624 | // If it's a power of two immediate then we emit an shl instead |
| | 1625 | // TODO add similar checks for LHS |
| | 1626 | if (new_rhs == .immediate and math.isPowerOfTwo(new_rhs.immediate)) { |
| | 1627 | return try self.binOp(.shl, maybe_inst, new_lhs, .{ .immediate = math.log2(new_rhs.immediate) }, new_lhs_ty, Type.usize); |
| | 1628 | } |
| | 1629 | |
| | 1630 | return try self.binOpRegister(.mulx, maybe_inst, new_lhs, new_rhs, new_lhs_ty, new_rhs_ty); |
| | 1631 | } else { |
| | 1632 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| | 1633 | } |
| | 1634 | }, |
| | 1635 | else => unreachable, |
| | 1636 | } |
| | 1637 | }, |
| | 1638 | |
| | 1639 | .ptr_add => { |
| | 1640 | switch (lhs_ty.zigTypeTag()) { |
| | 1641 | .Pointer => { |
| | 1642 | const ptr_ty = lhs_ty; |
| | 1643 | const elem_ty = switch (ptr_ty.ptrSize()) { |
| | 1644 | .One => ptr_ty.childType().childType(), // ptr to array, so get array element type |
| | 1645 | else => ptr_ty.childType(), |
| | 1646 | }; |
| | 1647 | const elem_size = elem_ty.abiSize(self.target.*); |
| | 1648 | |
| | 1649 | if (elem_size == 1) { |
| | 1650 | const base_tag: Mir.Inst.Tag = switch (tag) { |
| | 1651 | .ptr_add => .add, |
| | 1652 | else => unreachable, |
| | 1653 | }; |
| | 1654 | |
| | 1655 | return try self.binOpRegister(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| | 1656 | } else { |
| | 1657 | // convert the offset into a byte offset by |
| | 1658 | // multiplying it with elem_size |
| | 1659 | |
| | 1660 | const offset = try self.binOp(.mul, null, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize); |
| | 1661 | const addr = try self.binOp(tag, null, lhs, offset, Type.initTag(.manyptr_u8), Type.usize); |
| | 1662 | return addr; |
| | 1663 | } |
| | 1664 | }, |
| | 1665 | else => unreachable, |
| | 1666 | } |
| | 1667 | }, |
| | 1668 | |
| | 1669 | .shl => { |
| | 1670 | const base_tag: Air.Inst.Tag = switch (tag) { |
| | 1671 | .shl => .shl_exact, |
| | 1672 | else => unreachable, |
| | 1673 | }; |
| | 1674 | |
| | 1675 | // Generate a shl_exact/shr_exact |
| | 1676 | const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| | 1677 | |
| | 1678 | // Truncate if necessary |
| | 1679 | switch (tag) { |
| | 1680 | .shl => switch (lhs_ty.zigTypeTag()) { |
| | 1681 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| | 1682 | .Int => { |
| | 1683 | const int_info = lhs_ty.intInfo(self.target.*); |
| | 1684 | if (int_info.bits <= 64) { |
| | 1685 | const result_reg = result.register; |
| | 1686 | try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); |
| | 1687 | return result; |
| | 1688 | } else { |
| | 1689 | return self.fail("TODO binary operations on integers > u64/i64", .{}); |
| | 1690 | } |
| | 1691 | }, |
| | 1692 | else => unreachable, |
| | 1693 | }, |
| | 1694 | else => unreachable, |
| | 1695 | } |
| | 1696 | }, |
| | 1697 | |
| | 1698 | .shl_exact => { |
| | 1699 | switch (lhs_ty.zigTypeTag()) { |
| | 1700 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| | 1701 | .Int => { |
| | 1702 | const int_info = lhs_ty.intInfo(self.target.*); |
| | 1703 | if (int_info.bits <= 64) { |
| | 1704 | const rhs_immediate_ok = rhs == .immediate; |
| | 1705 | |
| | 1706 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| | 1707 | .shl_exact => .sllx, |
| | 1708 | else => unreachable, |
| | 1709 | }; |
| | 1710 | |
| | 1711 | if (rhs_immediate_ok) { |
| | 1712 | return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); |
| | 1713 | } else { |
| | 1714 | return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| | 1715 | } |
| | 1716 | } else { |
| | 1717 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| | 1718 | } |
| | 1719 | }, |
| | 1720 | else => unreachable, |
| | 1721 | } |
| | 1722 | }, |
| | 1723 | |
| 1564 | else => return self.fail("TODO implement {} binOp for SPARCv9", .{tag}), | 1724 | else => return self.fail("TODO implement {} binOp for SPARCv9", .{tag}), |
| 1565 | } | 1725 | } |
| 1566 | } | 1726 | } |
| 1567 | | 1727 | |
| | 1728 | /// Don't call this function directly. Use binOp instead. |
| | 1729 | /// |
| | 1730 | /// Calling this function signals an intention to generate a Mir |
| | 1731 | /// instruction of the form |
| | 1732 | /// |
| | 1733 | /// op dest, lhs, #rhs_imm |
| | 1734 | /// |
| | 1735 | /// Set lhs_and_rhs_swapped to true iff inst.bin_op.lhs corresponds to |
| | 1736 | /// rhs and vice versa. This parameter is only used when maybe_inst != |
| | 1737 | /// null. |
| | 1738 | /// |
| | 1739 | /// Asserts that generating an instruction of that form is possible. |
| | 1740 | fn binOpImmediate( |
| | 1741 | self: *Self, |
| | 1742 | mir_tag: Mir.Inst.Tag, |
| | 1743 | maybe_inst: ?Air.Inst.Index, |
| | 1744 | lhs: MCValue, |
| | 1745 | rhs: MCValue, |
| | 1746 | lhs_ty: Type, |
| | 1747 | lhs_and_rhs_swapped: bool, |
| | 1748 | ) !MCValue { |
| | 1749 | const lhs_is_register = lhs == .register; |
| | 1750 | |
| | 1751 | const lhs_lock: ?RegisterLock = if (lhs_is_register) |
| | 1752 | self.register_manager.lockReg(lhs.register) |
| | 1753 | else |
| | 1754 | null; |
| | 1755 | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| | 1756 | |
| | 1757 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| | 1758 | |
| | 1759 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { |
| | 1760 | const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { |
| | 1761 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| | 1762 | break :inst Air.refToIndex( |
| | 1763 | if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, |
| | 1764 | ).?; |
| | 1765 | } else null; |
| | 1766 | |
| | 1767 | const reg = try self.register_manager.allocReg(track_inst); |
| | 1768 | |
| | 1769 | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| | 1770 | |
| | 1771 | break :blk reg; |
| | 1772 | }; |
| | 1773 | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); |
| | 1774 | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| | 1775 | |
| | 1776 | const dest_reg = switch (mir_tag) { |
| | 1777 | else => if (maybe_inst) |inst| blk: { |
| | 1778 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| | 1779 | |
| | 1780 | if (lhs_is_register and self.reuseOperand( |
| | 1781 | inst, |
| | 1782 | if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, |
| | 1783 | if (lhs_and_rhs_swapped) 1 else 0, |
| | 1784 | lhs, |
| | 1785 | )) { |
| | 1786 | break :blk lhs_reg; |
| | 1787 | } else { |
| | 1788 | break :blk try self.register_manager.allocReg(inst); |
| | 1789 | } |
| | 1790 | } else blk: { |
| | 1791 | break :blk try self.register_manager.allocReg(null); |
| | 1792 | }, |
| | 1793 | }; |
| | 1794 | |
| | 1795 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| | 1796 | |
| | 1797 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| | 1798 | .add, |
| | 1799 | .mulx, |
| | 1800 | .subcc, |
| | 1801 | => .{ |
| | 1802 | .arithmetic_3op = .{ |
| | 1803 | .is_imm = true, |
| | 1804 | .rd = dest_reg, |
| | 1805 | .rs1 = lhs_reg, |
| | 1806 | .rs2_or_imm = .{ .imm = @intCast(i13, rhs.immediate) }, |
| | 1807 | }, |
| | 1808 | }, |
| | 1809 | .sllx => .{ |
| | 1810 | .shift = .{ |
| | 1811 | .is_imm = true, |
| | 1812 | .width = ShiftWidth.shift64, |
| | 1813 | .rd = dest_reg, |
| | 1814 | .rs1 = lhs_reg, |
| | 1815 | .rs2_or_imm = .{ .imm = @intCast(u6, rhs.immediate) }, |
| | 1816 | }, |
| | 1817 | }, |
| | 1818 | else => unreachable, |
| | 1819 | }; |
| | 1820 | |
| | 1821 | _ = try self.addInst(.{ |
| | 1822 | .tag = mir_tag, |
| | 1823 | .data = mir_data, |
| | 1824 | }); |
| | 1825 | |
| | 1826 | return MCValue{ .register = dest_reg }; |
| | 1827 | } |
| | 1828 | |
| 1568 | /// Don't call this function directly. Use binOp instead. | 1829 | /// Don't call this function directly. Use binOp instead. |
| 1569 | /// | 1830 | /// |
| 1570 | /// Calling this function signals an intention to generate a Mir | 1831 | /// Calling this function signals an intention to generate a Mir |
| ... | @@ -1647,12 +1908,26 @@ fn binOpRegister( | ... | @@ -1647,12 +1908,26 @@ fn binOpRegister( |
| 1647 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); | 1908 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| 1648 | | 1909 | |
| 1649 | const mir_data: Mir.Inst.Data = switch (mir_tag) { | 1910 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 1650 | .subcc => .{ .arithmetic_3op = .{ | 1911 | .add, |
| 1651 | .is_imm = false, | 1912 | .mulx, |
| 1652 | .rd = dest_reg, | 1913 | .subcc, |
| 1653 | .rs1 = lhs_reg, | 1914 | => .{ |
| 1654 | .rs2_or_imm = .{ .rs2 = rhs_reg }, | 1915 | .arithmetic_3op = .{ |
| 1655 | } }, | 1916 | .is_imm = false, |
| | 1917 | .rd = dest_reg, |
| | 1918 | .rs1 = lhs_reg, |
| | 1919 | .rs2_or_imm = .{ .rs2 = rhs_reg }, |
| | 1920 | }, |
| | 1921 | }, |
| | 1922 | .sllx => .{ |
| | 1923 | .shift = .{ |
| | 1924 | .is_imm = false, |
| | 1925 | .width = ShiftWidth.shift64, |
| | 1926 | .rd = dest_reg, |
| | 1927 | .rs1 = lhs_reg, |
| | 1928 | .rs2_or_imm = .{ .rs2 = rhs_reg }, |
| | 1929 | }, |
| | 1930 | }, |
| 1656 | else => unreachable, | 1931 | else => unreachable, |
| 1657 | }; | 1932 | }; |
| 1658 | | 1933 | |
| ... | @@ -2672,6 +2947,77 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type | ... | @@ -2672,6 +2947,77 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2672 | } | 2947 | } |
| 2673 | } | 2948 | } |
| 2674 | | 2949 | |
| | 2950 | fn truncRegister( |
| | 2951 | self: *Self, |
| | 2952 | operand_reg: Register, |
| | 2953 | dest_reg: Register, |
| | 2954 | int_signedness: std.builtin.Signedness, |
| | 2955 | int_bits: u16, |
| | 2956 | ) !void { |
| | 2957 | switch (int_bits) { |
| | 2958 | 1...31, 33...63 => { |
| | 2959 | _ = try self.addInst(.{ |
| | 2960 | .tag = .sllx, |
| | 2961 | .data = .{ |
| | 2962 | .shift = .{ |
| | 2963 | .is_imm = true, |
| | 2964 | .width = ShiftWidth.shift64, |
| | 2965 | .rd = dest_reg, |
| | 2966 | .rs1 = operand_reg, |
| | 2967 | .rs2_or_imm = .{ .imm = @intCast(u6, 64 - int_bits) }, |
| | 2968 | }, |
| | 2969 | }, |
| | 2970 | }); |
| | 2971 | _ = try self.addInst(.{ |
| | 2972 | .tag = switch (int_signedness) { |
| | 2973 | .signed => .srax, |
| | 2974 | .unsigned => .srlx, |
| | 2975 | }, |
| | 2976 | .data = .{ |
| | 2977 | .shift = .{ |
| | 2978 | .is_imm = true, |
| | 2979 | .width = ShiftWidth.shift32, |
| | 2980 | .rd = dest_reg, |
| | 2981 | .rs1 = dest_reg, |
| | 2982 | .rs2_or_imm = .{ .imm = @intCast(u6, int_bits) }, |
| | 2983 | }, |
| | 2984 | }, |
| | 2985 | }); |
| | 2986 | }, |
| | 2987 | 32 => { |
| | 2988 | _ = try self.addInst(.{ |
| | 2989 | .tag = switch (int_signedness) { |
| | 2990 | .signed => .sra, |
| | 2991 | .unsigned => .srl, |
| | 2992 | }, |
| | 2993 | .data = .{ |
| | 2994 | .shift = .{ |
| | 2995 | .is_imm = true, |
| | 2996 | .width = ShiftWidth.shift32, |
| | 2997 | .rd = dest_reg, |
| | 2998 | .rs1 = operand_reg, |
| | 2999 | .rs2_or_imm = .{ .imm = 0 }, |
| | 3000 | }, |
| | 3001 | }, |
| | 3002 | }); |
| | 3003 | }, |
| | 3004 | 64 => { |
| | 3005 | _ = try self.addInst(.{ |
| | 3006 | .tag = .@"or", |
| | 3007 | .data = .{ |
| | 3008 | .arithmetic_3op = .{ |
| | 3009 | .is_imm = true, |
| | 3010 | .rd = dest_reg, |
| | 3011 | .rs1 = .g0, |
| | 3012 | .rs2_or_imm = .{ .rs2 = operand_reg }, |
| | 3013 | }, |
| | 3014 | }, |
| | 3015 | }); |
| | 3016 | }, |
| | 3017 | else => unreachable, |
| | 3018 | } |
| | 3019 | } |
| | 3020 | |
| 2675 | /// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`. | 3021 | /// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`. |
| 2676 | fn wantSafety(self: *Self) bool { | 3022 | fn wantSafety(self: *Self) bool { |
| 2677 | return switch (self.bin_file.options.optimize_mode) { | 3023 | return switch (self.bin_file.options.optimize_mode) { |