Commit 964dbeb826

Robin Voetter <robin@voetter.nl>
2021-12-20 21:53:40
stage2: @subWithOverflow
1 parent 58d67a6
src/arch/aarch64/CodeGen.zig
@@ -522,6 +522,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
                     .slice           => try self.airSlice(inst),
 
                     .add_with_overflow => try self.airAddWithOverflow(inst),
+                    .sub_with_overflow => try self.airSubWithOverflow(inst),
                     .mul_with_overflow => try self.airMulWithOverflow(inst),
 
                     .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
@@ -977,6 +978,11 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
     return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch});
 }
 
+fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
+    _ = inst;
+    return self.fail("TODO implement airSubResultWithOverflow for {}", .{self.target.cpu.arch});
+}
+
 fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
     _ = inst;
     return self.fail("TODO implement airMulResultWithOverflow for {}", .{self.target.cpu.arch});
src/arch/arm/CodeGen.zig
@@ -520,6 +520,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
                     .slice           => try self.airSlice(inst),
 
                     .add_with_overflow => try self.airAddWithOverflow(inst),
+                    .sub_with_overflow => try self.airSubWithOverflow(inst),
                     .mul_with_overflow => try self.airMulWithOverflow(inst),
 
                     .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
@@ -1007,6 +1008,11 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
     return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch});
 }
 
+fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
+    _ = inst;
+    return self.fail("TODO implement airSubResultWithOverflow for {}", .{self.target.cpu.arch});
+}
+
 fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
     _ = inst;
     return self.fail("TODO implement airMulResultWithOverflow for {}", .{self.target.cpu.arch});
src/arch/riscv64/CodeGen.zig
@@ -501,6 +501,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
                     .slice           => try self.airSlice(inst),
 
                     .add_with_overflow => try self.airAddWithOverflow(inst),
+                    .sub_with_overflow => try self.airSubWithOverflow(inst),
                     .mul_with_overflow => try self.airMulWithOverflow(inst),
 
                     .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
@@ -922,6 +923,11 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
     return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch});
 }
 
+fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
+    _ = inst;
+    return self.fail("TODO implement airSubResultWithOverflow for {}", .{self.target.cpu.arch});
+}
+
 fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
     _ = inst;
     return self.fail("TODO implement airMulResultWithOverflow for {}", .{self.target.cpu.arch});
src/arch/x86_64/CodeGen.zig
@@ -554,6 +554,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
                     .slice           => try self.airSlice(inst),
 
                     .add_with_overflow => try self.airAddWithOverflow(inst),
+                    .sub_with_overflow => try self.airSubWithOverflow(inst),
                     .mul_with_overflow => try self.airMulWithOverflow(inst),
 
                     .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
@@ -1036,6 +1037,11 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
     return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch});
 }
 
+fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
+    _ = inst;
+    return self.fail("TODO implement airSubResultWithOverflow for {}", .{self.target.cpu.arch});
+}
+
 fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
     _ = inst;
     return self.fail("TODO implement airMulResultWithOverflow for {}", .{self.target.cpu.arch});
src/codegen/c.zig
@@ -1157,6 +1157,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
             .shl_sat => try airSatOp(f, inst, "shls_"),
 
             .add_with_overflow => try airAddWithOverflow(f, inst),
+            .sub_with_overflow => try airSubWithOverflow(f, inst),
             .mul_with_overflow => try airMulWithOverflow(f, inst),
 
             .min => try airMinMax(f, inst, "<"),
@@ -1874,6 +1875,12 @@ fn airAddWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue {
     return f.fail("TODO add with overflow", .{});
 }
 
+fn airSubWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue {
+    _ = f;
+    _ = inst;
+    return f.fail("TODO sub with overflow", .{});
+}
+
 fn airMulWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue {
     _ = f;
     _ = inst;
src/codegen/llvm.zig
@@ -1719,6 +1719,7 @@ pub const FuncGen = struct {
                 .slice     => try self.airSlice(inst),
 
                 .add_with_overflow => try self.airOverflow(inst, "llvm.sadd.with.overflow", "llvm.uadd.with.overflow"),
+                .sub_with_overflow => try self.airOverflow(inst, "llvm.ssub.with.overflow", "llvm.usub.with.overflow"),
                 .mul_with_overflow => try self.airOverflow(inst, "llvm.smul.with.overflow", "llvm.umul.with.overflow"),
 
                 .bit_and, .bool_and => try self.airAnd(inst),
src/Air.zig
@@ -141,6 +141,12 @@ pub const Inst = struct {
         /// of the operation.
         /// Uses the `pl_op` field with payload `Bin`.
         add_with_overflow,
+        /// Integer subtraction with overflow. Both operands are guaranteed to be the same type,
+        /// and the result is bool. The wrapped value is written to the pointer given by the in
+        /// operand of the `pl_op` field. Payload is `Bin` with `lhs` and `rhs` the relevant types
+        /// of the operation.
+        /// Uses the `pl_op` field with payload `Bin`.
+        sub_with_overflow,
         /// Integer multiplication with overflow. Both operands are guaranteed to be the same type,
         /// and the result is bool. The wrapped value is written to the pointer given by the in
         /// operand of the `pl_op` field. Payload is `Bin` with `lhs` and `rhs` the relevant types
@@ -822,6 +828,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
         },
 
         .add_with_overflow,
+        .sub_with_overflow,
         .mul_with_overflow,
         => return Type.initTag(.bool),
     }
src/Liveness.zig
@@ -382,7 +382,12 @@ fn analyzeInst(
             const extra = a.air.extraData(Air.AtomicRmw, pl_op.payload).data;
             return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.operand, .none });
         },
-        .memset, .memcpy, .add_with_overflow, .mul_with_overflow => {
+        .memset,
+        .memcpy,
+        .add_with_overflow,
+        .sub_with_overflow,
+        .mul_with_overflow,
+         => {
             const pl_op = inst_datas[inst].pl_op;
             const extra = a.air.extraData(Air.Bin, pl_op.payload).data;
             return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.lhs, extra.rhs });
src/print_air.zig
@@ -231,6 +231,7 @@ const Writer = struct {
             .memset => try w.writeMemset(s, inst),
 
             .add_with_overflow,
+            .sub_with_overflow,
             .mul_with_overflow,
             => try w.writeOverflow(s, inst),
         }
src/Sema.zig
@@ -7365,16 +7365,27 @@ fn zirOverflowArithmetic(
                         }
 
                         const result = try lhs_val.intAddWithOverflow(rhs_val, dest_ty, sema.arena, target);
-                        const inst = try sema.addConstant(
-                            dest_ty,
-                            result.wrapped_result,
-                        );
-
-                        if (result.overflowed) {
-                            break :result .{ .overflowed = .yes, .wrapped = inst };
-                        } else {
-                            break :result .{ .overflowed = .no, .wrapped = inst };
+                        const inst = try sema.addConstant(dest_ty, result.wrapped_result);
+                        break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };
+                    }
+                }
+            },
+            .sub_with_overflow => {
+                // If the rhs is zero, then the result is lhs and no overflow occured.
+                // Otherwise, if either result is undefined, both results are undefined.
+                if (maybe_rhs_val) |rhs_val| {
+                    if (rhs_val.isUndef()) {
+                        break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) };
+                    } else if (rhs_val.compareWithZero(.eq)) {
+                        break :result .{ .overflowed = .no, .wrapped = lhs };
+                    } else if (maybe_lhs_val) |lhs_val| {
+                        if (lhs_val.isUndef()) {
+                            break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) };
                         }
+
+                        const result = try lhs_val.intSubWithOverflow(rhs_val, dest_ty, sema.arena, target);
+                        const inst = try sema.addConstant(dest_ty, result.wrapped_result);
+                        break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };
                     }
                 }
             },
@@ -7382,7 +7393,6 @@ fn zirOverflowArithmetic(
                 // If either of the arguments is zero, the result is zero and no overflow occured.
                 // If either of the arguments is one, the result is the other and no overflow occured.
                 // Otherwise, if either of the arguments is undefined, both results are undefined.
-
                 if (maybe_lhs_val) |lhs_val| {
                     if (!lhs_val.isUndef()) {
                         if (lhs_val.compareWithZero(.eq)) {
@@ -7410,20 +7420,11 @@ fn zirOverflowArithmetic(
                         }
 
                         const result = try lhs_val.intMulWithOverflow(rhs_val, dest_ty, sema.arena, target);
-                        const inst = try sema.addConstant(
-                            dest_ty,
-                            result.wrapped_result,
-                        );
-
-                        if (result.overflowed) {
-                            break :result .{ .overflowed = .yes, .wrapped = inst };
-                        } else {
-                            break :result .{ .overflowed = .no, .wrapped = inst };
-                        }
+                        const inst = try sema.addConstant(dest_ty, result.wrapped_result);
+                        break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };
                     }
                 }
             },
-            .sub_with_overflow,
             .shl_with_overflow,
             => return sema.fail(block, src, "TODO implement Sema.zirOverflowArithmetic for {}", .{zir_tag}),
             else => unreachable,
@@ -7432,6 +7433,7 @@ fn zirOverflowArithmetic(
         const air_tag: Air.Inst.Tag = switch (zir_tag) {
             .add_with_overflow => .add_with_overflow,
             .mul_with_overflow => .mul_with_overflow,
+            .sub_with_overflow => .sub_with_overflow,
             else => return sema.fail(block, src, "TODO implement runtime Sema.zirOverflowArithmetic for {}", .{zir_tag}),
         };
 
test/behavior/math.zig
@@ -495,3 +495,19 @@ test "@mulWithOverflow" {
     try expect(@mulWithOverflow(u8, a, b, &result));
     try expect(result == 236);
 }
+
+test "@subWithOverflow" {
+    var result: u8 = undefined;
+    try expect(@subWithOverflow(u8, 1, 2, &result));
+    try expect(result == 255);
+    try expect(!@subWithOverflow(u8, 1, 1, &result));
+    try expect(result == 0);
+
+    var a: u8 = 1;
+    var b: u8 = 2;
+    try expect(@subWithOverflow(u8, a, b, &result));
+    try expect(result == 255);
+    b = 1;
+    try expect(!@subWithOverflow(u8, a, b, &result));
+    try expect(result == 0);
+}
test/behavior/math_stage1.zig
@@ -6,14 +6,6 @@ const maxInt = std.math.maxInt;
 const minInt = std.math.minInt;
 const mem = std.mem;
 
-test "@subWithOverflow" {
-    var result: u8 = undefined;
-    try expect(@subWithOverflow(u8, 1, 2, &result));
-    try expect(result == 255);
-    try expect(!@subWithOverflow(u8, 1, 1, &result));
-    try expect(result == 0);
-}
-
 test "@shlWithOverflow" {
     var result: u16 = undefined;
     try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result));