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 {
952952 .neg,
953953 => try self.airUnaryMath(inst),
954954
955 .add_with_overflow => try self.airAddSubShlWithOverflow(inst),
956 .sub_with_overflow => try self.airAddSubShlWithOverflow(inst),
955 .add_with_overflow => try self.airAddSubWithOverflow(inst),
956 .sub_with_overflow => try self.airAddSubWithOverflow(inst),
957957 .mul_with_overflow => try self.airMulWithOverflow(inst),
958 .shl_with_overflow => try self.airAddSubShlWithOverflow(inst),
958 .shl_with_overflow => try self.airShlWithOverflow(inst),
959959
960960 .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 {
18511851 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
18521852}
18531853
1854fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1854fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
18551855 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
18561856 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
18571857 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
18581858 const tag = self.air.instructions.items(.tag)[inst];
18591859 const ty = self.air.typeOf(bin_op.lhs);
18601860 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", .{}),
18621862 .Int => {
18631863 try self.spillEflagsIfOccupied();
18641864
1865 if (tag == .shl_with_overflow) {
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) {
1865 const partial_mcv = switch (tag) {
18721866 .add_with_overflow => try self.genBinOp(null, .add, bin_op.lhs, bin_op.rhs),
18731867 .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 },
18811868 else => unreachable,
18821869 };
1883
18841870 const int_info = ty.intInfo(self.target.*);
1871 const cc: Condition = switch (int_info.signedness) {
1872 .unsigned => .c,
1873 .signed => .o,
1874 };
1875
18851876 if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) {
1886 const cc: Condition = switch (int_info.signedness) {
1887 .unsigned => .c,
1888 .signed => .o,
1889 };
1890 switch (partial) {
1877 switch (partial_mcv) {
18911878 .register => |reg| {
18921879 self.eflags_inst = inst;
18931880 break :result .{ .register_overflow = .{ .reg = reg, .eflags = cc } };
......@@ -1903,7 +1890,7 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
19031890 .{ .eflags = cc },
19041891 .{},
19051892 );
1906 try self.genSetStack(ty, dst_mcv.stack_offset, partial, .{});
1893 try self.genSetStack(ty, dst_mcv.stack_offset, partial_mcv, .{});
19071894 break :result dst_mcv;
19081895 }
19091896
......@@ -1913,7 +1900,78 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
19131900 const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*));
19141901 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
19181976 break :result .{ .stack_offset = stack_offset };
19191977 },
......@@ -1929,6 +1987,7 @@ fn genSetStackTruncatedOverflowCompare(
19291987 stack_offset: i32,
19301988 overflow_bit_offset: i32,
19311989 reg: Register,
1990 cc: Condition,
19321991) !void {
19331992 const reg_lock = self.register_manager.lockReg(reg);
19341993 defer if (reg_lock) |lock| self.register_manager.unlockReg(lock);
......@@ -1946,10 +2005,6 @@ fn genSetStackTruncatedOverflowCompare(
19462005 };
19472006
19482007 const overflow_reg = temp_regs[0];
1949 const cc: Condition = switch (int_info.signedness) {
1950 .signed => .o,
1951 .unsigned => .c,
1952 };
19532008 try self.asmSetccRegister(overflow_reg.to8(), cc);
19542009
19552010 const scratch_reg = temp_regs[1];
......@@ -1988,6 +2043,10 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
19882043 try self.spillEflagsIfOccupied();
19892044
19902045 const dst_info = dst_ty.intInfo(self.target.*);
2046 const cc: Condition = switch (dst_info.signedness) {
2047 .unsigned => .c,
2048 .signed => .o,
2049 };
19912050 if (dst_info.bits >= 8 and math.isPowerOfTwo(dst_info.bits)) {
19922051 var src_pl = Type.Payload.Bits{ .base = .{ .tag = switch (dst_info.signedness) {
19932052 .signed => .int_signed,
......@@ -2003,12 +2062,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
20032062 const lhs = try self.resolveInst(bin_op.lhs);
20042063 const rhs = try self.resolveInst(bin_op.rhs);
20052064
2006 const partial = try self.genMulDivBinOp(.mul, null, dst_ty, src_ty, lhs, rhs);
2007 const cc: Condition = switch (dst_info.signedness) {
2008 .unsigned => .c,
2009 .signed => .o,
2010 };
2011 switch (partial) {
2065 const partial_mcv = try self.genMulDivBinOp(.mul, null, dst_ty, src_ty, lhs, rhs);
2066 switch (partial_mcv) {
20122067 .register => |reg| {
20132068 self.eflags_inst = inst;
20142069 break :result .{ .register_overflow = .{ .reg = reg, .eflags = cc } };
......@@ -2024,7 +2079,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
20242079 .{ .eflags = cc },
20252080 .{},
20262081 );
2027 try self.genSetStack(dst_ty, dst_mcv.stack_offset, partial, .{});
2082 try self.genSetStack(dst_ty, dst_mcv.stack_offset, partial_mcv, .{});
20282083 break :result dst_mcv;
20292084 }
20302085
......@@ -2079,7 +2134,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
20792134 const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*));
20802135 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
20842139 break :result .{ .stack_offset = stack_offset };
20852140 },
test/behavior/eval.zig-1
......@@ -488,7 +488,6 @@ test "comptime bitwise operators" {
488488test "comptime shlWithOverflow" {
489489 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
490490 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
491 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
492491
493492 const ct_shifted = @shlWithOverflow(~@as(u64, 0), 16)[0];
494493 var a = ~@as(u64, 0);
test/behavior/math.zig-2
......@@ -1237,8 +1237,6 @@ fn testShlTrunc(x: u16) !void {
12371237}
12381238
12391239test "exact shift left" {
1240 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1241
12421240 try testShlExact(0b00110101);
12431241 comptime try testShlExact(0b00110101);
12441242}