authorgravatar for koachan@protonmail.comKoakuma <koachan@protonmail.com> 2022-05-14 22:19:37+07:00
committergravatar for koachan@protonmail.comKoakuma <koachan@protonmail.com> 2022-05-16 23:30:54+07:00
log7822426ff2a7c23bf8090d6a1f77e54fca8d62c8
treed5cee2cdaab543366143227c3237c1bd67afeda3
parentccf438e4de75299a235f1f4f88a7de62779cc19a

stage2: sparc64: Implement airSliceElemVal


3 files changed, 378 insertions(+), 16 deletions(-)

src/arch/sparc64/CodeGen.zig+360-14
......@@ -30,6 +30,7 @@ const build_options = @import("build_options");
3030const bits = @import("bits.zig");
3131const abi = @import("abi.zig");
3232const Instruction = bits.Instruction;
33const ShiftWidth = Instruction.ShiftWidth;
3334const Register = bits.Register;
3435
3536const Self = @This();
......@@ -637,7 +638,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
637638 .ptr_slice_ptr_ptr => @panic("TODO try self.airPtrSlicePtrPtr(inst)"),
638639
639640 .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),
641642 .slice_elem_ptr => @panic("TODO try self.airSliceElemPtr(inst)"),
642643 .ptr_elem_val => @panic("TODO try self.airPtrElemVal(inst)"),
643644 .ptr_elem_ptr => @panic("TODO try self.airPtrElemPtr(inst)"),
......@@ -1374,16 +1375,48 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
13741375 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
13751376}
13761377
1377fn airStore(self: *Self, inst: Air.Inst.Index) !void {
1378fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
1379 const is_volatile = false; // TODO
13781380 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);
13831381
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);
13851386
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 });
13871420}
13881421
13891422fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
......@@ -1407,6 +1440,18 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
14071440 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
14081441}
14091442
1443fn 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
14101455fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
14111456 _ = self;
14121457 _ = inst;
......@@ -1561,10 +1606,226 @@ fn binOp(
15611606 }
15621607 },
15631608
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
15641724 else => return self.fail("TODO implement {} binOp for SPARCv9", .{tag}),
15651725 }
15661726}
15671727
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.
1740fn 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
15681829/// Don't call this function directly. Use binOp instead.
15691830///
15701831/// Calling this function signals an intention to generate a Mir
......@@ -1647,12 +1908,26 @@ fn binOpRegister(
16471908 if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
16481909
16491910 const mir_data: Mir.Inst.Data = switch (mir_tag) {
1650 .subcc => .{ .arithmetic_3op = .{
1651 .is_imm = false,
1652 .rd = dest_reg,
1653 .rs1 = lhs_reg,
1654 .rs2_or_imm = .{ .rs2 = rhs_reg },
1655 } },
1911 .add,
1912 .mulx,
1913 .subcc,
1914 => .{
1915 .arithmetic_3op = .{
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 },
16561931 else => unreachable,
16571932 };
16581933
......@@ -2672,6 +2947,77 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
26722947 }
26732948}
26742949
2950fn 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
26753021/// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`.
26763022fn wantSafety(self: *Self) bool {
26773023 return switch (self.bin_file.options.optimize_mode) {
src/arch/sparc64/Emit.zig+7
......@@ -94,6 +94,8 @@ pub fn emitMir(
9494
9595 .@"or" => try emit.mirArithmetic3Op(inst),
9696
97 .mulx => @panic("TODO implement sparc64 mulx"),
98
9799 .nop => try emit.mirNop(),
98100
99101 .@"return" => try emit.mirArithmetic2Op(inst),
......@@ -103,7 +105,12 @@ pub fn emitMir(
103105
104106 .sethi => try emit.mirSethi(inst),
105107
108 .sll => @panic("TODO implement sparc64 sll"),
109 .srl => @panic("TODO implement sparc64 srl"),
110 .sra => @panic("TODO implement sparc64 sra"),
106111 .sllx => @panic("TODO implement sparc64 sllx"),
112 .srlx => @panic("TODO implement sparc64 srlx"),
113 .srax => @panic("TODO implement sparc64 srax"),
107114
108115 .stb => try emit.mirArithmetic3Op(inst),
109116 .sth => try emit.mirArithmetic3Op(inst),
src/arch/sparc64/Mir.zig+11-2
......@@ -74,6 +74,11 @@ pub const Inst = struct {
7474 // TODO add other operations.
7575 @"or",
7676
77 /// A.37 Multiply and Divide (64-bit)
78 /// This uses the arithmetic_3op field.
79 // TODO add other operations.
80 mulx,
81
7782 /// A.40 No Operation
7883 /// This uses the nop field.
7984 nop,
......@@ -93,8 +98,12 @@ pub const Inst = struct {
9398
9499 /// A.49 Shift
95100 /// This uses the shift field.
96 // TODO add other operations.
101 sll,
102 srl,
103 sra,
97104 sllx,
105 srlx,
106 srax,
98107
99108 /// A.54 Store Integer
100109 /// This uses the arithmetic_3op field.
......@@ -210,7 +219,7 @@ pub const Inst = struct {
210219 /// if is_imm true then it uses the imm field of rs2_or_imm,
211220 /// otherwise it uses rs2 field.
212221 ///
213 /// Used by e.g. add, sub
222 /// Used by e.g. sllx
214223 shift: struct {
215224 is_imm: bool,
216225 width: Instruction.ShiftWidth,