authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-04-02 05:47:38-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-04-02 06:11:12-04:00
logf4359531b154951f9c242c487249169499f0852d
tree998bf1472c044903d21d301d362f1c650cc69152
parent0e289cc8269c9c18358b511564718972808d2efe

x86_64: implement shl with overflow


3 files changed, 94 insertions(+), 42 deletions(-)

src/arch/x86_64/CodeGen.zig+94-39
...@@ -952,10 +952,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -952,10 +952,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
952 .neg,952 .neg,
953 => try self.airUnaryMath(inst),953 => try self.airUnaryMath(inst),
954954
955 .add_with_overflow => try self.airAddSubShlWithOverflow(inst),955 .add_with_overflow => try self.airAddSubWithOverflow(inst),
956 .sub_with_overflow => try self.airAddSubShlWithOverflow(inst),956 .sub_with_overflow => try self.airAddSubWithOverflow(inst),
957 .mul_with_overflow => try self.airMulWithOverflow(inst),957 .mul_with_overflow => try self.airMulWithOverflow(inst),
958 .shl_with_overflow => try self.airAddSubShlWithOverflow(inst),958 .shl_with_overflow => try self.airShlWithOverflow(inst),
959959
960 .div_float, .div_trunc, .div_floor, .div_exact => try self.airMulDivBinOp(inst),960 .div_float, .div_trunc, .div_floor, .div_exact => try self.airMulDivBinOp(inst),
961961
...@@ -1851,43 +1851,30 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {...@@ -1851,43 +1851,30 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
1851 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1851 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1852}1852}
18531853
1854fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {1854fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1855 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;1855 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1856 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;1856 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1857 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {1857 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1858 const tag = self.air.instructions.items(.tag)[inst];1858 const tag = self.air.instructions.items(.tag)[inst];
1859 const ty = self.air.typeOf(bin_op.lhs);1859 const ty = self.air.typeOf(bin_op.lhs);
1860 switch (ty.zigTypeTag()) {1860 switch (ty.zigTypeTag()) {
1861 .Vector => return self.fail("TODO implement add/sub/shl with overflow for Vector type", .{}),1861 .Vector => return self.fail("TODO implement add/sub with overflow for Vector type", .{}),
1862 .Int => {1862 .Int => {
1863 try self.spillEflagsIfOccupied();1863 try self.spillEflagsIfOccupied();
18641864
1865 if (tag == .shl_with_overflow) {1865 const partial_mcv = switch (tag) {
1866 try self.spillRegisters(&.{.rcx});
1867 // cf/of don't work for shifts other than 1
1868 return self.fail("TODO implement shl_with_overflow for x86_64", .{});
1869 }
1870
1871 const partial: MCValue = switch (tag) {
1872 .add_with_overflow => try self.genBinOp(null, .add, bin_op.lhs, bin_op.rhs),1866 .add_with_overflow => try self.genBinOp(null, .add, bin_op.lhs, bin_op.rhs),
1873 .sub_with_overflow => try self.genBinOp(null, .sub, bin_op.lhs, bin_op.rhs),1867 .sub_with_overflow => try self.genBinOp(null, .sub, bin_op.lhs, bin_op.rhs),
1874 .shl_with_overflow => blk: {
1875 try self.register_manager.getReg(.rcx, null);
1876 const lhs = try self.resolveInst(bin_op.lhs);
1877 const rhs = try self.resolveInst(bin_op.rhs);
1878 const shift_ty = self.air.typeOf(bin_op.rhs);
1879 break :blk try self.genShiftBinOp(.shl, null, lhs, rhs, ty, shift_ty);
1880 },
1881 else => unreachable,1868 else => unreachable,
1882 };1869 };
1883
1884 const int_info = ty.intInfo(self.target.*);1870 const int_info = ty.intInfo(self.target.*);
1871 const cc: Condition = switch (int_info.signedness) {
1872 .unsigned => .c,
1873 .signed => .o,
1874 };
1875
1885 if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) {1876 if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) {
1886 const cc: Condition = switch (int_info.signedness) {1877 switch (partial_mcv) {
1887 .unsigned => .c,
1888 .signed => .o,
1889 };
1890 switch (partial) {
1891 .register => |reg| {1878 .register => |reg| {
1892 self.eflags_inst = inst;1879 self.eflags_inst = inst;
1893 break :result .{ .register_overflow = .{ .reg = reg, .eflags = cc } };1880 break :result .{ .register_overflow = .{ .reg = reg, .eflags = cc } };
...@@ -1903,7 +1890,7 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1903,7 +1890,7 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1903 .{ .eflags = cc },1890 .{ .eflags = cc },
1904 .{},1891 .{},
1905 );1892 );
1906 try self.genSetStack(ty, dst_mcv.stack_offset, partial, .{});1893 try self.genSetStack(ty, dst_mcv.stack_offset, partial_mcv, .{});
1907 break :result dst_mcv;1894 break :result dst_mcv;
1908 }1895 }
19091896
...@@ -1913,7 +1900,78 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1913,7 +1900,78 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1913 const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*));1900 const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*));
1914 const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align));1901 const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align));
19151902
1916 try self.genSetStackTruncatedOverflowCompare(ty, stack_offset, overflow_bit_offset, partial.register);1903 try self.genSetStackTruncatedOverflowCompare(ty, stack_offset, overflow_bit_offset, partial_mcv.register, cc);
1904
1905 break :result .{ .stack_offset = stack_offset };
1906 },
1907 else => unreachable,
1908 }
1909 };
1910 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1911}
1912
1913fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1914 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1915 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1916 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1917 const lhs_ty = self.air.typeOf(bin_op.lhs);
1918 const rhs_ty = self.air.typeOf(bin_op.rhs);
1919 switch (lhs_ty.zigTypeTag()) {
1920 .Vector => return self.fail("TODO implement shl with overflow for Vector type", .{}),
1921 .Int => {
1922 try self.spillEflagsIfOccupied();
1923
1924 try self.register_manager.getReg(.rcx, null);
1925 const lhs = try self.resolveInst(bin_op.lhs);
1926 const rhs = try self.resolveInst(bin_op.rhs);
1927
1928 const int_info = lhs_ty.intInfo(self.target.*);
1929
1930 const partial_mcv = try self.genShiftBinOp(.shl, null, lhs, rhs, lhs_ty, rhs_ty);
1931 const partial_lock = switch (partial_mcv) {
1932 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
1933 else => null,
1934 };
1935 defer if (partial_lock) |lock| self.register_manager.unlockReg(lock);
1936
1937 const tmp_mcv = try self.genShiftBinOp(.shr, null, partial_mcv, rhs, lhs_ty, rhs_ty);
1938 const tmp_lock = switch (tmp_mcv) {
1939 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
1940 else => null,
1941 };
1942 defer if (tmp_lock) |lock| self.register_manager.unlockReg(lock);
1943
1944 try self.genBinOpMir(.cmp, lhs_ty, tmp_mcv, lhs);
1945 const cc = Condition.ne;
1946
1947 if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) {
1948 switch (partial_mcv) {
1949 .register => |reg| {
1950 self.eflags_inst = inst;
1951 break :result .{ .register_overflow = .{ .reg = reg, .eflags = cc } };
1952 },
1953 else => {},
1954 }
1955
1956 const abi_size = @intCast(i32, lhs_ty.abiSize(self.target.*));
1957 const dst_mcv = try self.allocRegOrMem(inst, false);
1958 try self.genSetStack(
1959 Type.u1,
1960 dst_mcv.stack_offset - abi_size,
1961 .{ .eflags = cc },
1962 .{},
1963 );
1964 try self.genSetStack(lhs_ty, dst_mcv.stack_offset, partial_mcv, .{});
1965 break :result dst_mcv;
1966 }
1967
1968 const tuple_ty = self.air.typeOfIndex(inst);
1969 const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*));
1970 const tuple_align = tuple_ty.abiAlignment(self.target.*);
1971 const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*));
1972 const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align));
1973
1974 try self.genSetStackTruncatedOverflowCompare(lhs_ty, stack_offset, overflow_bit_offset, partial_mcv.register, cc);
19171975
1918 break :result .{ .stack_offset = stack_offset };1976 break :result .{ .stack_offset = stack_offset };
1919 },1977 },
...@@ -1929,6 +1987,7 @@ fn genSetStackTruncatedOverflowCompare(...@@ -1929,6 +1987,7 @@ fn genSetStackTruncatedOverflowCompare(
1929 stack_offset: i32,1987 stack_offset: i32,
1930 overflow_bit_offset: i32,1988 overflow_bit_offset: i32,
1931 reg: Register,1989 reg: Register,
1990 cc: Condition,
1932) !void {1991) !void {
1933 const reg_lock = self.register_manager.lockReg(reg);1992 const reg_lock = self.register_manager.lockReg(reg);
1934 defer if (reg_lock) |lock| self.register_manager.unlockReg(lock);1993 defer if (reg_lock) |lock| self.register_manager.unlockReg(lock);
...@@ -1946,10 +2005,6 @@ fn genSetStackTruncatedOverflowCompare(...@@ -1946,10 +2005,6 @@ fn genSetStackTruncatedOverflowCompare(
1946 };2005 };
19472006
1948 const overflow_reg = temp_regs[0];2007 const overflow_reg = temp_regs[0];
1949 const cc: Condition = switch (int_info.signedness) {
1950 .signed => .o,
1951 .unsigned => .c,
1952 };
1953 try self.asmSetccRegister(overflow_reg.to8(), cc);2008 try self.asmSetccRegister(overflow_reg.to8(), cc);
19542009
1955 const scratch_reg = temp_regs[1];2010 const scratch_reg = temp_regs[1];
...@@ -1988,6 +2043,10 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1988,6 +2043,10 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1988 try self.spillEflagsIfOccupied();2043 try self.spillEflagsIfOccupied();
19892044
1990 const dst_info = dst_ty.intInfo(self.target.*);2045 const dst_info = dst_ty.intInfo(self.target.*);
2046 const cc: Condition = switch (dst_info.signedness) {
2047 .unsigned => .c,
2048 .signed => .o,
2049 };
1991 if (dst_info.bits >= 8 and math.isPowerOfTwo(dst_info.bits)) {2050 if (dst_info.bits >= 8 and math.isPowerOfTwo(dst_info.bits)) {
1992 var src_pl = Type.Payload.Bits{ .base = .{ .tag = switch (dst_info.signedness) {2051 var src_pl = Type.Payload.Bits{ .base = .{ .tag = switch (dst_info.signedness) {
1993 .signed => .int_signed,2052 .signed => .int_signed,
...@@ -2003,12 +2062,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2003,12 +2062,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2003 const lhs = try self.resolveInst(bin_op.lhs);2062 const lhs = try self.resolveInst(bin_op.lhs);
2004 const rhs = try self.resolveInst(bin_op.rhs);2063 const rhs = try self.resolveInst(bin_op.rhs);
20052064
2006 const partial = try self.genMulDivBinOp(.mul, null, dst_ty, src_ty, lhs, rhs);2065 const partial_mcv = try self.genMulDivBinOp(.mul, null, dst_ty, src_ty, lhs, rhs);
2007 const cc: Condition = switch (dst_info.signedness) {2066 switch (partial_mcv) {
2008 .unsigned => .c,
2009 .signed => .o,
2010 };
2011 switch (partial) {
2012 .register => |reg| {2067 .register => |reg| {
2013 self.eflags_inst = inst;2068 self.eflags_inst = inst;
2014 break :result .{ .register_overflow = .{ .reg = reg, .eflags = cc } };2069 break :result .{ .register_overflow = .{ .reg = reg, .eflags = cc } };
...@@ -2024,7 +2079,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2024,7 +2079,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2024 .{ .eflags = cc },2079 .{ .eflags = cc },
2025 .{},2080 .{},
2026 );2081 );
2027 try self.genSetStack(dst_ty, dst_mcv.stack_offset, partial, .{});2082 try self.genSetStack(dst_ty, dst_mcv.stack_offset, partial_mcv, .{});
2028 break :result dst_mcv;2083 break :result dst_mcv;
2029 }2084 }
20302085
...@@ -2079,7 +2134,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2079,7 +2134,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2079 const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*));2134 const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*));
2080 const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align));2135 const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align));
20812136
2082 try self.genSetStackTruncatedOverflowCompare(dst_ty, stack_offset, overflow_bit_offset, dst_reg);2137 try self.genSetStackTruncatedOverflowCompare(dst_ty, stack_offset, overflow_bit_offset, dst_reg, cc);
20832138
2084 break :result .{ .stack_offset = stack_offset };2139 break :result .{ .stack_offset = stack_offset };
2085 },2140 },
test/behavior/eval.zig-1
...@@ -488,7 +488,6 @@ test "comptime bitwise operators" {...@@ -488,7 +488,6 @@ test "comptime bitwise operators" {
488test "comptime shlWithOverflow" {488test "comptime shlWithOverflow" {
489 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO489 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
490 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO490 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
491 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
492491
493 const ct_shifted = @shlWithOverflow(~@as(u64, 0), 16)[0];492 const ct_shifted = @shlWithOverflow(~@as(u64, 0), 16)[0];
494 var a = ~@as(u64, 0);493 var a = ~@as(u64, 0);
test/behavior/math.zig-2
...@@ -1237,8 +1237,6 @@ fn testShlTrunc(x: u16) !void {...@@ -1237,8 +1237,6 @@ fn testShlTrunc(x: u16) !void {
1237}1237}
12381238
1239test "exact shift left" {1239test "exact shift left" {
1240 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1241
1242 try testShlExact(0b00110101);1240 try testShlExact(0b00110101);
1243 comptime try testShlExact(0b00110101);1241 comptime try testShlExact(0b00110101);
1244}1242}