| author | |
| committer | |
| log | 964dbeb82623515b8392c8c7cb9317246812174e |
| tree | 8bedd9ddf607ce6b3ca983702934c3c950959185 |
| parent | 58d67a6718d5d0673389fa19f5bb20812b4bb22a |
12 files changed, 85 insertions(+), 30 deletions(-)
src/Air.zig+7| ... | ... | @@ -141,6 +141,12 @@ pub const Inst = struct { |
| 141 | 141 | /// of the operation. |
| 142 | 142 | /// Uses the `pl_op` field with payload `Bin`. |
| 143 | 143 | add_with_overflow, |
| 144 | /// Integer subtraction with overflow. Both operands are guaranteed to be the same type, | |
| 145 | /// and the result is bool. The wrapped value is written to the pointer given by the in | |
| 146 | /// operand of the `pl_op` field. Payload is `Bin` with `lhs` and `rhs` the relevant types | |
| 147 | /// of the operation. | |
| 148 | /// Uses the `pl_op` field with payload `Bin`. | |
| 149 | sub_with_overflow, | |
| 144 | 150 | /// Integer multiplication with overflow. Both operands are guaranteed to be the same type, |
| 145 | 151 | /// and the result is bool. The wrapped value is written to the pointer given by the in |
| 146 | 152 | /// 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 { |
| 822 | 828 | }, |
| 823 | 829 | |
| 824 | 830 | .add_with_overflow, |
| 831 | .sub_with_overflow, | |
| 825 | 832 | .mul_with_overflow, |
| 826 | 833 | => return Type.initTag(.bool), |
| 827 | 834 | } |
src/Liveness.zig+6-1| ... | ... | @@ -382,7 +382,12 @@ fn analyzeInst( |
| 382 | 382 | const extra = a.air.extraData(Air.AtomicRmw, pl_op.payload).data; |
| 383 | 383 | return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.operand, .none }); |
| 384 | 384 | }, |
| 385 | .memset, .memcpy, .add_with_overflow, .mul_with_overflow => { | |
| 385 | .memset, | |
| 386 | .memcpy, | |
| 387 | .add_with_overflow, | |
| 388 | .sub_with_overflow, | |
| 389 | .mul_with_overflow, | |
| 390 | => { | |
| 386 | 391 | const pl_op = inst_datas[inst].pl_op; |
| 387 | 392 | const extra = a.air.extraData(Air.Bin, pl_op.payload).data; |
| 388 | 393 | return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.lhs, extra.rhs }); |
src/Sema.zig+23-21| ... | ... | @@ -7365,16 +7365,27 @@ fn zirOverflowArithmetic( |
| 7365 | 7365 | } |
| 7366 | 7366 | |
| 7367 | 7367 | const result = try lhs_val.intAddWithOverflow(rhs_val, dest_ty, sema.arena, target); |
| 7368 | const inst = try sema.addConstant( | |
| 7369 | dest_ty, | |
| 7370 | result.wrapped_result, | |
| 7371 | ); | |
| 7372 | ||
| 7373 | if (result.overflowed) { | |
| 7374 | break :result .{ .overflowed = .yes, .wrapped = inst }; | |
| 7375 | } else { | |
| 7376 | break :result .{ .overflowed = .no, .wrapped = inst }; | |
| 7368 | const inst = try sema.addConstant(dest_ty, result.wrapped_result); | |
| 7369 | break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst }; | |
| 7370 | } | |
| 7371 | } | |
| 7372 | }, | |
| 7373 | .sub_with_overflow => { | |
| 7374 | // If the rhs is zero, then the result is lhs and no overflow occured. | |
| 7375 | // Otherwise, if either result is undefined, both results are undefined. | |
| 7376 | if (maybe_rhs_val) |rhs_val| { | |
| 7377 | if (rhs_val.isUndef()) { | |
| 7378 | break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) }; | |
| 7379 | } else if (rhs_val.compareWithZero(.eq)) { | |
| 7380 | break :result .{ .overflowed = .no, .wrapped = lhs }; | |
| 7381 | } else if (maybe_lhs_val) |lhs_val| { | |
| 7382 | if (lhs_val.isUndef()) { | |
| 7383 | break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) }; | |
| 7377 | 7384 | } |
| 7385 | ||
| 7386 | const result = try lhs_val.intSubWithOverflow(rhs_val, dest_ty, sema.arena, target); | |
| 7387 | const inst = try sema.addConstant(dest_ty, result.wrapped_result); | |
| 7388 | break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst }; | |
| 7378 | 7389 | } |
| 7379 | 7390 | } |
| 7380 | 7391 | }, |
| ... | ... | @@ -7382,7 +7393,6 @@ fn zirOverflowArithmetic( |
| 7382 | 7393 | // If either of the arguments is zero, the result is zero and no overflow occured. |
| 7383 | 7394 | // If either of the arguments is one, the result is the other and no overflow occured. |
| 7384 | 7395 | // Otherwise, if either of the arguments is undefined, both results are undefined. |
| 7385 | ||
| 7386 | 7396 | if (maybe_lhs_val) |lhs_val| { |
| 7387 | 7397 | if (!lhs_val.isUndef()) { |
| 7388 | 7398 | if (lhs_val.compareWithZero(.eq)) { |
| ... | ... | @@ -7410,20 +7420,11 @@ fn zirOverflowArithmetic( |
| 7410 | 7420 | } |
| 7411 | 7421 | |
| 7412 | 7422 | const result = try lhs_val.intMulWithOverflow(rhs_val, dest_ty, sema.arena, target); |
| 7413 | const inst = try sema.addConstant( | |
| 7414 | dest_ty, | |
| 7415 | result.wrapped_result, | |
| 7416 | ); | |
| 7417 | ||
| 7418 | if (result.overflowed) { | |
| 7419 | break :result .{ .overflowed = .yes, .wrapped = inst }; | |
| 7420 | } else { | |
| 7421 | break :result .{ .overflowed = .no, .wrapped = inst }; | |
| 7422 | } | |
| 7423 | const inst = try sema.addConstant(dest_ty, result.wrapped_result); | |
| 7424 | break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst }; | |
| 7423 | 7425 | } |
| 7424 | 7426 | } |
| 7425 | 7427 | }, |
| 7426 | .sub_with_overflow, | |
| 7427 | 7428 | .shl_with_overflow, |
| 7428 | 7429 | => return sema.fail(block, src, "TODO implement Sema.zirOverflowArithmetic for {}", .{zir_tag}), |
| 7429 | 7430 | else => unreachable, |
| ... | ... | @@ -7432,6 +7433,7 @@ fn zirOverflowArithmetic( |
| 7432 | 7433 | const air_tag: Air.Inst.Tag = switch (zir_tag) { |
| 7433 | 7434 | .add_with_overflow => .add_with_overflow, |
| 7434 | 7435 | .mul_with_overflow => .mul_with_overflow, |
| 7436 | .sub_with_overflow => .sub_with_overflow, | |
| 7435 | 7437 | else => return sema.fail(block, src, "TODO implement runtime Sema.zirOverflowArithmetic for {}", .{zir_tag}), |
| 7436 | 7438 | }; |
| 7437 | 7439 |
src/arch/aarch64/CodeGen.zig+6| ... | ... | @@ -522,6 +522,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 522 | 522 | .slice => try self.airSlice(inst), |
| 523 | 523 | |
| 524 | 524 | .add_with_overflow => try self.airAddWithOverflow(inst), |
| 525 | .sub_with_overflow => try self.airSubWithOverflow(inst), | |
| 525 | 526 | .mul_with_overflow => try self.airMulWithOverflow(inst), |
| 526 | 527 | |
| 527 | 528 | .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 { |
| 977 | 978 | return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch}); |
| 978 | 979 | } |
| 979 | 980 | |
| 981 | fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | |
| 982 | _ = inst; | |
| 983 | return self.fail("TODO implement airSubResultWithOverflow for {}", .{self.target.cpu.arch}); | |
| 984 | } | |
| 985 | ||
| 980 | 986 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 981 | 987 | _ = inst; |
| 982 | 988 | return self.fail("TODO implement airMulResultWithOverflow for {}", .{self.target.cpu.arch}); |
src/arch/arm/CodeGen.zig+6| ... | ... | @@ -520,6 +520,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 520 | 520 | .slice => try self.airSlice(inst), |
| 521 | 521 | |
| 522 | 522 | .add_with_overflow => try self.airAddWithOverflow(inst), |
| 523 | .sub_with_overflow => try self.airSubWithOverflow(inst), | |
| 523 | 524 | .mul_with_overflow => try self.airMulWithOverflow(inst), |
| 524 | 525 | |
| 525 | 526 | .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 { |
| 1007 | 1008 | return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch}); |
| 1008 | 1009 | } |
| 1009 | 1010 | |
| 1011 | fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | |
| 1012 | _ = inst; | |
| 1013 | return self.fail("TODO implement airSubResultWithOverflow for {}", .{self.target.cpu.arch}); | |
| 1014 | } | |
| 1015 | ||
| 1010 | 1016 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1011 | 1017 | _ = inst; |
| 1012 | 1018 | return self.fail("TODO implement airMulResultWithOverflow for {}", .{self.target.cpu.arch}); |
src/arch/riscv64/CodeGen.zig+6| ... | ... | @@ -501,6 +501,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 501 | 501 | .slice => try self.airSlice(inst), |
| 502 | 502 | |
| 503 | 503 | .add_with_overflow => try self.airAddWithOverflow(inst), |
| 504 | .sub_with_overflow => try self.airSubWithOverflow(inst), | |
| 504 | 505 | .mul_with_overflow => try self.airMulWithOverflow(inst), |
| 505 | 506 | |
| 506 | 507 | .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 { |
| 922 | 923 | return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch}); |
| 923 | 924 | } |
| 924 | 925 | |
| 926 | fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | |
| 927 | _ = inst; | |
| 928 | return self.fail("TODO implement airSubResultWithOverflow for {}", .{self.target.cpu.arch}); | |
| 929 | } | |
| 930 | ||
| 925 | 931 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 926 | 932 | _ = inst; |
| 927 | 933 | return self.fail("TODO implement airMulResultWithOverflow for {}", .{self.target.cpu.arch}); |
src/arch/x86_64/CodeGen.zig+6| ... | ... | @@ -554,6 +554,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 554 | 554 | .slice => try self.airSlice(inst), |
| 555 | 555 | |
| 556 | 556 | .add_with_overflow => try self.airAddWithOverflow(inst), |
| 557 | .sub_with_overflow => try self.airSubWithOverflow(inst), | |
| 557 | 558 | .mul_with_overflow => try self.airMulWithOverflow(inst), |
| 558 | 559 | |
| 559 | 560 | .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 { |
| 1036 | 1037 | return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch}); |
| 1037 | 1038 | } |
| 1038 | 1039 | |
| 1040 | fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | |
| 1041 | _ = inst; | |
| 1042 | return self.fail("TODO implement airSubResultWithOverflow for {}", .{self.target.cpu.arch}); | |
| 1043 | } | |
| 1044 | ||
| 1039 | 1045 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1040 | 1046 | _ = inst; |
| 1041 | 1047 | return self.fail("TODO implement airMulResultWithOverflow for {}", .{self.target.cpu.arch}); |
src/codegen/c.zig+7| ... | ... | @@ -1157,6 +1157,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 1157 | 1157 | .shl_sat => try airSatOp(f, inst, "shls_"), |
| 1158 | 1158 | |
| 1159 | 1159 | .add_with_overflow => try airAddWithOverflow(f, inst), |
| 1160 | .sub_with_overflow => try airSubWithOverflow(f, inst), | |
| 1160 | 1161 | .mul_with_overflow => try airMulWithOverflow(f, inst), |
| 1161 | 1162 | |
| 1162 | 1163 | .min => try airMinMax(f, inst, "<"), |
| ... | ... | @@ -1874,6 +1875,12 @@ fn airAddWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1874 | 1875 | return f.fail("TODO add with overflow", .{}); |
| 1875 | 1876 | } |
| 1876 | 1877 | |
| 1878 | fn airSubWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 1879 | _ = f; | |
| 1880 | _ = inst; | |
| 1881 | return f.fail("TODO sub with overflow", .{}); | |
| 1882 | } | |
| 1883 | ||
| 1877 | 1884 | fn airMulWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1878 | 1885 | _ = f; |
| 1879 | 1886 | _ = inst; |
src/codegen/llvm.zig+1| ... | ... | @@ -1719,6 +1719,7 @@ pub const FuncGen = struct { |
| 1719 | 1719 | .slice => try self.airSlice(inst), |
| 1720 | 1720 | |
| 1721 | 1721 | .add_with_overflow => try self.airOverflow(inst, "llvm.sadd.with.overflow", "llvm.uadd.with.overflow"), |
| 1722 | .sub_with_overflow => try self.airOverflow(inst, "llvm.ssub.with.overflow", "llvm.usub.with.overflow"), | |
| 1722 | 1723 | .mul_with_overflow => try self.airOverflow(inst, "llvm.smul.with.overflow", "llvm.umul.with.overflow"), |
| 1723 | 1724 | |
| 1724 | 1725 | .bit_and, .bool_and => try self.airAnd(inst), |
src/print_air.zig+1| ... | ... | @@ -231,6 +231,7 @@ const Writer = struct { |
| 231 | 231 | .memset => try w.writeMemset(s, inst), |
| 232 | 232 | |
| 233 | 233 | .add_with_overflow, |
| 234 | .sub_with_overflow, | |
| 234 | 235 | .mul_with_overflow, |
| 235 | 236 | => try w.writeOverflow(s, inst), |
| 236 | 237 | } |
test/behavior/math.zig+16| ... | ... | @@ -495,3 +495,19 @@ test "@mulWithOverflow" { |
| 495 | 495 | try expect(@mulWithOverflow(u8, a, b, &result)); |
| 496 | 496 | try expect(result == 236); |
| 497 | 497 | } |
| 498 | ||
| 499 | test "@subWithOverflow" { | |
| 500 | var result: u8 = undefined; | |
| 501 | try expect(@subWithOverflow(u8, 1, 2, &result)); | |
| 502 | try expect(result == 255); | |
| 503 | try expect(!@subWithOverflow(u8, 1, 1, &result)); | |
| 504 | try expect(result == 0); | |
| 505 | ||
| 506 | var a: u8 = 1; | |
| 507 | var b: u8 = 2; | |
| 508 | try expect(@subWithOverflow(u8, a, b, &result)); | |
| 509 | try expect(result == 255); | |
| 510 | b = 1; | |
| 511 | try expect(!@subWithOverflow(u8, a, b, &result)); | |
| 512 | try expect(result == 0); | |
| 513 | } |
test/behavior/math_stage1.zig-8| ... | ... | @@ -6,14 +6,6 @@ const maxInt = std.math.maxInt; |
| 6 | 6 | const minInt = std.math.minInt; |
| 7 | 7 | const mem = std.mem; |
| 8 | 8 | |
| 9 | test "@subWithOverflow" { | |
| 10 | var result: u8 = undefined; | |
| 11 | try expect(@subWithOverflow(u8, 1, 2, &result)); | |
| 12 | try expect(result == 255); | |
| 13 | try expect(!@subWithOverflow(u8, 1, 1, &result)); | |
| 14 | try expect(result == 0); | |
| 15 | } | |
| 16 | ||
| 17 | 9 | test "@shlWithOverflow" { |
| 18 | 10 | var result: u16 = undefined; |
| 19 | 11 | try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); |