From 964dbeb82623515b8392c8c7cb9317246812174e Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Mon, 20 Dec 2021 21:53:40 +0100 Subject: [PATCH] stage2: @subWithOverflow --- src/Air.zig | 7 ++++++ src/Liveness.zig | 7 +++++- src/Sema.zig | 44 ++++++++++++++++++----------------- src/arch/aarch64/CodeGen.zig | 6 +++++ src/arch/arm/CodeGen.zig | 6 +++++ src/arch/riscv64/CodeGen.zig | 6 +++++ src/arch/x86_64/CodeGen.zig | 6 +++++ src/codegen/c.zig | 7 ++++++ src/codegen/llvm.zig | 1 + src/print_air.zig | 1 + test/behavior/math.zig | 16 +++++++++++++ test/behavior/math_stage1.zig | 8 ------- 12 files changed, 85 insertions(+), 30 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index 5d54be7392..912e70daed 100644 --- a/src/Air.zig +++ b/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), } diff --git a/src/Liveness.zig b/src/Liveness.zig index 6859f64660..ad0ce7ffb9 100644 --- a/src/Liveness.zig +++ b/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 }); diff --git a/src/Sema.zig b/src/Sema.zig index 38183d1052..2eb661503e 100644 --- a/src/Sema.zig +++ b/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}), }; diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index e34475f1ee..b381116a51 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/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}); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 779ce52036..f887810d9a 100644 --- a/src/arch/arm/CodeGen.zig +++ b/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}); diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index c8f7173b1c..56904206ab 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/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}); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index fe8d8b55f7..be26372031 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/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}); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index f6a3105760..68b700db17 100644 --- a/src/codegen/c.zig +++ b/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; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 07fe138786..4089a39cb3 100644 --- a/src/codegen/llvm.zig +++ b/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), diff --git a/src/print_air.zig b/src/print_air.zig index 2204d16bd6..d6bfaac4bc 100644 --- a/src/print_air.zig +++ b/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), } diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 963fe3f0d5..50a7267c93 100644 --- a/test/behavior/math.zig +++ b/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); +} diff --git a/test/behavior/math_stage1.zig b/test/behavior/math_stage1.zig index 381f89634b..139593b9f5 100644 --- a/test/behavior/math_stage1.zig +++ b/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));