| author | |
| committer | |
| log | c47ed0c912d2f445710fe4486fa071dd63601989 |
| tree | 3272549828e1ca5fbb65b43a6cad1ea6e200cbd4 |
| parent | ddd2ef822f99979d3ea61583a91ab236942e6367 |
13 files changed, 164 insertions(+), 37 deletions(-)
src/Air.zig+9-1| ... | @@ -141,6 +141,12 @@ pub const Inst = struct { | ... | @@ -141,6 +141,12 @@ pub const Inst = struct { |
| 141 | /// of the operation. | 141 | /// of the operation. |
| 142 | /// Uses the `pl_op` field with payload `Bin`. | 142 | /// Uses the `pl_op` field with payload `Bin`. |
| 143 | add_with_overflow, | 143 | add_with_overflow, |
| 144 | /// Integer multiplication 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 | mul_with_overflow, | ||
| 144 | /// Allocates stack local memory. | 150 | /// Allocates stack local memory. |
| 145 | /// Uses the `ty` field. | 151 | /// Uses the `ty` field. |
| 146 | alloc, | 152 | alloc, |
| ... | @@ -815,7 +821,9 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { | ... | @@ -815,7 +821,9 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 815 | return ptr_ty.elemType(); | 821 | return ptr_ty.elemType(); |
| 816 | }, | 822 | }, |
| 817 | 823 | ||
| 818 | .add_with_overflow => return Type.initTag(.bool), | 824 | .add_with_overflow, |
| 825 | .mul_with_overflow, | ||
| 826 | => return Type.initTag(.bool), | ||
| 819 | } | 827 | } |
| 820 | } | 828 | } |
| 821 | 829 |
src/Liveness.zig+1-1| ... | @@ -382,7 +382,7 @@ fn analyzeInst( | ... | @@ -382,7 +382,7 @@ fn analyzeInst( |
| 382 | const extra = a.air.extraData(Air.AtomicRmw, pl_op.payload).data; | 382 | const extra = a.air.extraData(Air.AtomicRmw, pl_op.payload).data; |
| 383 | return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.operand, .none }); | 383 | return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.operand, .none }); |
| 384 | }, | 384 | }, |
| 385 | .memset, .memcpy, .add_with_overflow => { | 385 | .memset, .memcpy, .add_with_overflow, .mul_with_overflow => { |
| 386 | const pl_op = inst_datas[inst].pl_op; | 386 | const pl_op = inst_datas[inst].pl_op; |
| 387 | const extra = a.air.extraData(Air.Bin, pl_op.payload).data; | 387 | const extra = a.air.extraData(Air.Bin, pl_op.payload).data; |
| 388 | return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.lhs, extra.rhs }); | 388 | return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.lhs, extra.rhs }); |
src/Sema.zig+52-4| ... | @@ -7343,8 +7343,8 @@ fn zirOverflowArithmetic( | ... | @@ -7343,8 +7343,8 @@ fn zirOverflowArithmetic( |
| 7343 | overflowed: enum { yes, no, undef }, | 7343 | overflowed: enum { yes, no, undef }, |
| 7344 | wrapped: Air.Inst.Ref, | 7344 | wrapped: Air.Inst.Ref, |
| 7345 | } = result: { | 7345 | } = result: { |
| 7346 | const air_tag: Air.Inst.Tag = switch (zir_tag) { | 7346 | switch (zir_tag) { |
| 7347 | .add_with_overflow => blk: { | 7347 | .add_with_overflow => { |
| 7348 | // If either of the arguments is zero, `false` is returned and the other is stored | 7348 | // If either of the arguments is zero, `false` is returned and the other is stored |
| 7349 | // to the result, even if it is undefined.. | 7349 | // to the result, even if it is undefined.. |
| 7350 | // Otherwise, if either of the argument is undefined, undefined is returned. | 7350 | // Otherwise, if either of the argument is undefined, undefined is returned. |
| ... | @@ -7377,14 +7377,62 @@ fn zirOverflowArithmetic( | ... | @@ -7377,14 +7377,62 @@ fn zirOverflowArithmetic( |
| 7377 | } | 7377 | } |
| 7378 | } | 7378 | } |
| 7379 | } | 7379 | } |
| 7380 | }, | ||
| 7381 | .mul_with_overflow => { | ||
| 7382 | // If either of the arguments is zero, the result is zero and no overflow occured. | ||
| 7383 | // If either of the arguments is one, the result is the other and no overflow occured. | ||
| 7384 | // Otherwise, if either of the arguments is undefined, both results are undefined. | ||
| 7385 | |||
| 7386 | if (maybe_lhs_val) |lhs_val| { | ||
| 7387 | if (!lhs_val.isUndef()) { | ||
| 7388 | if (lhs_val.compareWithZero(.eq)) { | ||
| 7389 | break :result .{ .overflowed = .no, .wrapped = lhs }; | ||
| 7390 | } else if (lhs_val.compare(.eq, Value.one, dest_ty)) { | ||
| 7391 | break :result .{ .overflowed = .no, .wrapped = rhs }; | ||
| 7392 | } | ||
| 7393 | } | ||
| 7394 | } | ||
| 7380 | 7395 | ||
| 7381 | break :blk .add_with_overflow; | 7396 | if (maybe_rhs_val) |rhs_val| { |
| 7397 | if (!rhs_val.isUndef()) { | ||
| 7398 | if (rhs_val.compareWithZero(.eq)) { | ||
| 7399 | break :result .{ .overflowed = .no, .wrapped = rhs }; | ||
| 7400 | } else if (rhs_val.compare(.eq, Value.one, dest_ty)) { | ||
| 7401 | break :result .{ .overflowed = .no, .wrapped = lhs }; | ||
| 7402 | } | ||
| 7403 | } | ||
| 7404 | } | ||
| 7405 | |||
| 7406 | if (maybe_lhs_val) |lhs_val| { | ||
| 7407 | if (maybe_rhs_val) |rhs_val| { | ||
| 7408 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | ||
| 7409 | break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) }; | ||
| 7410 | } | ||
| 7411 | |||
| 7412 | 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 | } | ||
| 7424 | } | ||
| 7382 | }, | 7425 | }, |
| 7383 | .sub_with_overflow, | 7426 | .sub_with_overflow, |
| 7384 | .mul_with_overflow, | ||
| 7385 | .shl_with_overflow, | 7427 | .shl_with_overflow, |
| 7386 | => return sema.fail(block, src, "TODO implement Sema.zirOverflowArithmetic for {}", .{zir_tag}), | 7428 | => return sema.fail(block, src, "TODO implement Sema.zirOverflowArithmetic for {}", .{zir_tag}), |
| 7387 | else => unreachable, | 7429 | else => unreachable, |
| 7430 | } | ||
| 7431 | |||
| 7432 | const air_tag: Air.Inst.Tag = switch (zir_tag) { | ||
| 7433 | .add_with_overflow => .add_with_overflow, | ||
| 7434 | .mul_with_overflow => .mul_with_overflow, | ||
| 7435 | else => return sema.fail(block, src, "TODO implement runtime Sema.zirOverflowArithmetic for {}", .{zir_tag}), | ||
| 7388 | }; | 7436 | }; |
| 7389 | 7437 | ||
| 7390 | try sema.requireRuntimeBlock(block, src); | 7438 | try sema.requireRuntimeBlock(block, src); |
src/arch/aarch64/CodeGen.zig+6| ... | @@ -522,6 +522,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -522,6 +522,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 522 | .slice => try self.airSlice(inst), | 522 | .slice => try self.airSlice(inst), |
| 523 | 523 | ||
| 524 | .add_with_overflow => try self.airAddWithOverflow(inst), | 524 | .add_with_overflow => try self.airAddWithOverflow(inst), |
| 525 | .mul_with_overflow => try self.airMulWithOverflow(inst), | ||
| 525 | 526 | ||
| 526 | .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), | 527 | .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), |
| 527 | 528 | ||
| ... | @@ -976,6 +977,11 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -976,6 +977,11 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 976 | return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch}); | 977 | return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch}); |
| 977 | } | 978 | } |
| 978 | 979 | ||
| 980 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ||
| 981 | _ = inst; | ||
| 982 | return self.fail("TODO implement airMulResultWithOverflow for {}", .{self.target.cpu.arch}); | ||
| 983 | } | ||
| 984 | |||
| 979 | fn airDiv(self: *Self, inst: Air.Inst.Index) !void { | 985 | fn airDiv(self: *Self, inst: Air.Inst.Index) !void { |
| 980 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 986 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 981 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch}); | 987 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div 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,6 +520,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 520 | .slice => try self.airSlice(inst), | 520 | .slice => try self.airSlice(inst), |
| 521 | 521 | ||
| 522 | .add_with_overflow => try self.airAddWithOverflow(inst), | 522 | .add_with_overflow => try self.airAddWithOverflow(inst), |
| 523 | .mul_with_overflow => try self.airMulWithOverflow(inst), | ||
| 523 | 524 | ||
| 524 | .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), | 525 | .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), |
| 525 | 526 | ||
| ... | @@ -1006,6 +1007,11 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1006,6 +1007,11 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1006 | return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch}); | 1007 | return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch}); |
| 1007 | } | 1008 | } |
| 1008 | 1009 | ||
| 1010 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ||
| 1011 | _ = inst; | ||
| 1012 | return self.fail("TODO implement airMulResultWithOverflow for {}", .{self.target.cpu.arch}); | ||
| 1013 | } | ||
| 1014 | |||
| 1009 | fn airDiv(self: *Self, inst: Air.Inst.Index) !void { | 1015 | fn airDiv(self: *Self, inst: Air.Inst.Index) !void { |
| 1010 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1016 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1011 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch}); | 1017 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div 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,6 +501,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 501 | .slice => try self.airSlice(inst), | 501 | .slice => try self.airSlice(inst), |
| 502 | 502 | ||
| 503 | .add_with_overflow => try self.airAddWithOverflow(inst), | 503 | .add_with_overflow => try self.airAddWithOverflow(inst), |
| 504 | .mul_with_overflow => try self.airMulWithOverflow(inst), | ||
| 504 | 505 | ||
| 505 | .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), | 506 | .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), |
| 506 | 507 | ||
| ... | @@ -921,6 +922,11 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -921,6 +922,11 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 921 | return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch}); | 922 | return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch}); |
| 922 | } | 923 | } |
| 923 | 924 | ||
| 925 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ||
| 926 | _ = inst; | ||
| 927 | return self.fail("TODO implement airMulResultWithOverflow for {}", .{self.target.cpu.arch}); | ||
| 928 | } | ||
| 929 | |||
| 924 | fn airDiv(self: *Self, inst: Air.Inst.Index) !void { | 930 | fn airDiv(self: *Self, inst: Air.Inst.Index) !void { |
| 925 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 931 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 926 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch}); | 932 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div 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,6 +554,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 554 | .slice => try self.airSlice(inst), | 554 | .slice => try self.airSlice(inst), |
| 555 | 555 | ||
| 556 | .add_with_overflow => try self.airAddWithOverflow(inst), | 556 | .add_with_overflow => try self.airAddWithOverflow(inst), |
| 557 | .mul_with_overflow => try self.airMulWithOverflow(inst), | ||
| 557 | 558 | ||
| 558 | .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), | 559 | .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), |
| 559 | 560 | ||
| ... | @@ -1035,6 +1036,11 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1035,6 +1036,11 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1035 | return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch}); | 1036 | return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch}); |
| 1036 | } | 1037 | } |
| 1037 | 1038 | ||
| 1039 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ||
| 1040 | _ = inst; | ||
| 1041 | return self.fail("TODO implement airMulResultWithOverflow for {}", .{self.target.cpu.arch}); | ||
| 1042 | } | ||
| 1043 | |||
| 1038 | fn airDiv(self: *Self, inst: Air.Inst.Index) !void { | 1044 | fn airDiv(self: *Self, inst: Air.Inst.Index) !void { |
| 1039 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1045 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1040 | const result: MCValue = if (self.liveness.isUnused(inst)) | 1046 | const result: MCValue = if (self.liveness.isUnused(inst)) |
src/codegen/c.zig+7| ... | @@ -1157,6 +1157,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO | ... | @@ -1157,6 +1157,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 1157 | .shl_sat => try airSatOp(f, inst, "shls_"), | 1157 | .shl_sat => try airSatOp(f, inst, "shls_"), |
| 1158 | 1158 | ||
| 1159 | .add_with_overflow => try airAddWithOverflow(f, inst), | 1159 | .add_with_overflow => try airAddWithOverflow(f, inst), |
| 1160 | .mul_with_overflow => try airMulWithOverflow(f, inst), | ||
| 1160 | 1161 | ||
| 1161 | .min => try airMinMax(f, inst, "<"), | 1162 | .min => try airMinMax(f, inst, "<"), |
| 1162 | .max => try airMinMax(f, inst, ">"), | 1163 | .max => try airMinMax(f, inst, ">"), |
| ... | @@ -1873,6 +1874,12 @@ fn airAddWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -1873,6 +1874,12 @@ fn airAddWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1873 | return f.fail("TODO add with overflow", .{}); | 1874 | return f.fail("TODO add with overflow", .{}); |
| 1874 | } | 1875 | } |
| 1875 | 1876 | ||
| 1877 | fn airMulWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue { | ||
| 1878 | _ = f; | ||
| 1879 | _ = inst; | ||
| 1880 | return f.fail("TODO mul with overflow", .{}); | ||
| 1881 | } | ||
| 1882 | |||
| 1876 | fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { | 1883 | fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { |
| 1877 | if (f.liveness.isUnused(inst)) | 1884 | if (f.liveness.isUnused(inst)) |
| 1878 | return CValue.none; | 1885 | return CValue.none; |
src/codegen/llvm.zig+9-6| ... | @@ -1714,7 +1714,8 @@ pub const FuncGen = struct { | ... | @@ -1714,7 +1714,8 @@ pub const FuncGen = struct { |
| 1714 | .max => try self.airMax(inst), | 1714 | .max => try self.airMax(inst), |
| 1715 | .slice => try self.airSlice(inst), | 1715 | .slice => try self.airSlice(inst), |
| 1716 | 1716 | ||
| 1717 | .add_with_overflow => try self.airAddWithOverflow(inst), | 1717 | .add_with_overflow => try self.airOverflow(inst, "llvm.sadd.with.overflow", "llvm.uadd.with.overflow"), |
| 1718 | .mul_with_overflow => try self.airOverflow(inst, "llvm.smul.with.overflow", "llvm.umul.with.overflow"), | ||
| 1718 | 1719 | ||
| 1719 | .bit_and, .bool_and => try self.airAnd(inst), | 1720 | .bit_and, .bool_and => try self.airAnd(inst), |
| 1720 | .bit_or, .bool_or => try self.airOr(inst), | 1721 | .bit_or, .bool_or => try self.airOr(inst), |
| ... | @@ -3136,7 +3137,12 @@ pub const FuncGen = struct { | ... | @@ -3136,7 +3137,12 @@ pub const FuncGen = struct { |
| 3136 | } | 3137 | } |
| 3137 | } | 3138 | } |
| 3138 | 3139 | ||
| 3139 | fn airAddWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | 3140 | fn airOverflow( |
| 3141 | self: *FuncGen, | ||
| 3142 | inst: Air.Inst.Index, | ||
| 3143 | signed_intrinsic: []const u8, | ||
| 3144 | unsigned_intrinsic: []const u8, | ||
| 3145 | ) !?*const llvm.Value { | ||
| 3140 | if (self.liveness.isUnused(inst)) | 3146 | if (self.liveness.isUnused(inst)) |
| 3141 | return null; | 3147 | return null; |
| 3142 | 3148 | ||
| ... | @@ -3150,10 +3156,7 @@ pub const FuncGen = struct { | ... | @@ -3150,10 +3156,7 @@ pub const FuncGen = struct { |
| 3150 | const ptr_ty = self.air.typeOf(pl_op.operand); | 3156 | const ptr_ty = self.air.typeOf(pl_op.operand); |
| 3151 | const lhs_ty = self.air.typeOf(extra.lhs); | 3157 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 3152 | 3158 | ||
| 3153 | const intrinsic_name: []const u8 = if (lhs_ty.isSignedInt()) | 3159 | const intrinsic_name = if (lhs_ty.isSignedInt()) signed_intrinsic else unsigned_intrinsic; |
| 3154 | "llvm.sadd.with.overflow" | ||
| 3155 | else | ||
| 3156 | "llvm.uadd.with.overflow"; | ||
| 3157 | 3160 | ||
| 3158 | const llvm_lhs_ty = try self.dg.llvmType(lhs_ty); | 3161 | const llvm_lhs_ty = try self.dg.llvmType(lhs_ty); |
| 3159 | 3162 |
src/print_air.zig+5-2| ... | @@ -229,7 +229,10 @@ const Writer = struct { | ... | @@ -229,7 +229,10 @@ const Writer = struct { |
| 229 | .atomic_rmw => try w.writeAtomicRmw(s, inst), | 229 | .atomic_rmw => try w.writeAtomicRmw(s, inst), |
| 230 | .memcpy => try w.writeMemcpy(s, inst), | 230 | .memcpy => try w.writeMemcpy(s, inst), |
| 231 | .memset => try w.writeMemset(s, inst), | 231 | .memset => try w.writeMemset(s, inst), |
| 232 | .add_with_overflow => try w.writeAddWithOverflow(s, inst), | 232 | |
| 233 | .add_with_overflow, | ||
| 234 | .mul_with_overflow, | ||
| 235 | => try w.writeOverflow(s, inst), | ||
| 233 | } | 236 | } |
| 234 | } | 237 | } |
| 235 | 238 | ||
| ... | @@ -350,7 +353,7 @@ const Writer = struct { | ... | @@ -350,7 +353,7 @@ const Writer = struct { |
| 350 | try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) }); | 353 | try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) }); |
| 351 | } | 354 | } |
| 352 | 355 | ||
| 353 | fn writeAddWithOverflow(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { | 356 | fn writeOverflow(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { |
| 354 | const pl_op = w.air.instructions.items(.data)[inst].pl_op; | 357 | const pl_op = w.air.instructions.items(.data)[inst].pl_op; |
| 355 | const extra = w.air.extraData(Air.Bin, pl_op.payload).data; | 358 | const extra = w.air.extraData(Air.Bin, pl_op.payload).data; |
| 356 | 359 |
src/value.zig+33-15| ... | @@ -2130,20 +2130,13 @@ pub const Value = extern union { | ... | @@ -2130,20 +2130,13 @@ pub const Value = extern union { |
| 2130 | return fromBigInt(arena, result_bigint.toConst()); | 2130 | return fromBigInt(arena, result_bigint.toConst()); |
| 2131 | } | 2131 | } |
| 2132 | 2132 | ||
| 2133 | /// Supports both floats and ints; handles undefined. | 2133 | pub fn intMulWithOverflow( |
| 2134 | pub fn numberMulWrap( | ||
| 2135 | lhs: Value, | 2134 | lhs: Value, |
| 2136 | rhs: Value, | 2135 | rhs: Value, |
| 2137 | ty: Type, | 2136 | ty: Type, |
| 2138 | arena: Allocator, | 2137 | arena: Allocator, |
| 2139 | target: Target, | 2138 | target: Target, |
| 2140 | ) !Value { | 2139 | ) !OverflowArithmeticResult { |
| 2141 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); | ||
| 2142 | |||
| 2143 | if (ty.isAnyFloat()) { | ||
| 2144 | return floatMul(lhs, rhs, ty, arena); | ||
| 2145 | } | ||
| 2146 | |||
| 2147 | const info = ty.intInfo(target); | 2140 | const info = ty.intInfo(target); |
| 2148 | 2141 | ||
| 2149 | var lhs_space: Value.BigIntSpace = undefined; | 2142 | var lhs_space: Value.BigIntSpace = undefined; |
| ... | @@ -2152,16 +2145,42 @@ pub const Value = extern union { | ... | @@ -2152,16 +2145,42 @@ pub const Value = extern union { |
| 2152 | const rhs_bigint = rhs.toBigInt(&rhs_space); | 2145 | const rhs_bigint = rhs.toBigInt(&rhs_space); |
| 2153 | const limbs = try arena.alloc( | 2146 | const limbs = try arena.alloc( |
| 2154 | std.math.big.Limb, | 2147 | std.math.big.Limb, |
| 2155 | std.math.big.int.calcTwosCompLimbCount(info.bits), | 2148 | lhs_bigint.limbs.len + rhs_bigint.limbs.len, |
| 2156 | ); | 2149 | ); |
| 2157 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | 2150 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; |
| 2158 | var limbs_buffer = try arena.alloc( | 2151 | var limbs_buffer = try arena.alloc( |
| 2159 | std.math.big.Limb, | 2152 | std.math.big.Limb, |
| 2160 | std.math.big.int.calcMulWrapLimbsBufferLen(info.bits, lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1), | 2153 | std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1), |
| 2161 | ); | 2154 | ); |
| 2162 | defer arena.free(limbs_buffer); | 2155 | result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, arena); |
| 2163 | result_bigint.mulWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits, limbs_buffer, arena); | 2156 | |
| 2164 | return fromBigInt(arena, result_bigint.toConst()); | 2157 | const overflowed = !result_bigint.toConst().fitsInTwosComp(info.signedness, info.bits); |
| 2158 | if (overflowed) { | ||
| 2159 | result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits); | ||
| 2160 | } | ||
| 2161 | |||
| 2162 | return OverflowArithmeticResult{ | ||
| 2163 | .overflowed = overflowed, | ||
| 2164 | .wrapped_result = try fromBigInt(arena, result_bigint.toConst()), | ||
| 2165 | }; | ||
| 2166 | } | ||
| 2167 | |||
| 2168 | /// Supports both floats and ints; handles undefined. | ||
| 2169 | pub fn numberMulWrap( | ||
| 2170 | lhs: Value, | ||
| 2171 | rhs: Value, | ||
| 2172 | ty: Type, | ||
| 2173 | arena: Allocator, | ||
| 2174 | target: Target, | ||
| 2175 | ) !Value { | ||
| 2176 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); | ||
| 2177 | |||
| 2178 | if (ty.isAnyFloat()) { | ||
| 2179 | return floatMul(lhs, rhs, ty, arena); | ||
| 2180 | } | ||
| 2181 | |||
| 2182 | const overflow_result = try intMulWithOverflow(lhs, rhs, ty, arena, target); | ||
| 2183 | return overflow_result.wrapped_result; | ||
| 2165 | } | 2184 | } |
| 2166 | 2185 | ||
| 2167 | /// Supports integers only; asserts neither operand is undefined. | 2186 | /// Supports integers only; asserts neither operand is undefined. |
| ... | @@ -2194,7 +2213,6 @@ pub const Value = extern union { | ... | @@ -2194,7 +2213,6 @@ pub const Value = extern union { |
| 2194 | std.math.big.Limb, | 2213 | std.math.big.Limb, |
| 2195 | std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1), | 2214 | std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1), |
| 2196 | ); | 2215 | ); |
| 2197 | defer arena.free(limbs_buffer); | ||
| 2198 | result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, arena); | 2216 | result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, arena); |
| 2199 | result_bigint.saturate(result_bigint.toConst(), info.signedness, info.bits); | 2217 | result_bigint.saturate(result_bigint.toConst(), info.signedness, info.bits); |
| 2200 | return fromBigInt(arena, result_bigint.toConst()); | 2218 | return fromBigInt(arena, result_bigint.toConst()); |
test/behavior/math.zig+24| ... | @@ -451,6 +451,14 @@ test "@addWithOverflow" { | ... | @@ -451,6 +451,14 @@ test "@addWithOverflow" { |
| 451 | try expect(result == 94); | 451 | try expect(result == 94); |
| 452 | try expect(!@addWithOverflow(u8, 100, 150, &result)); | 452 | try expect(!@addWithOverflow(u8, 100, 150, &result)); |
| 453 | try expect(result == 250); | 453 | try expect(result == 250); |
| 454 | |||
| 455 | var a: u8 = 200; | ||
| 456 | var b: u8 = 99; | ||
| 457 | try expect(@addWithOverflow(u8, a, b, &result)); | ||
| 458 | try expect(result == 43); | ||
| 459 | b = 55; | ||
| 460 | try expect(!@addWithOverflow(u8, a, b, &result)); | ||
| 461 | try expect(result == 255); | ||
| 454 | } | 462 | } |
| 455 | 463 | ||
| 456 | test "small int addition" { | 464 | test "small int addition" { |
| ... | @@ -471,3 +479,19 @@ test "small int addition" { | ... | @@ -471,3 +479,19 @@ test "small int addition" { |
| 471 | 479 | ||
| 472 | try expect(result == 0); | 480 | try expect(result == 0); |
| 473 | } | 481 | } |
| 482 | |||
| 483 | test "@mulWithOverflow" { | ||
| 484 | var result: u8 = undefined; | ||
| 485 | try expect(@mulWithOverflow(u8, 86, 3, &result)); | ||
| 486 | try expect(result == 2); | ||
| 487 | try expect(!@mulWithOverflow(u8, 85, 3, &result)); | ||
| 488 | try expect(result == 255); | ||
| 489 | |||
| 490 | var a: u8 = 123; | ||
| 491 | var b: u8 = 2; | ||
| 492 | try expect(!@mulWithOverflow(u8, a, b, &result)); | ||
| 493 | try expect(result == 246); | ||
| 494 | b = 4; | ||
| 495 | try expect(@mulWithOverflow(u8, a, b, &result)); | ||
| 496 | try expect(result == 236); | ||
| 497 | } |
test/behavior/math_stage1.zig-8| ... | @@ -6,14 +6,6 @@ const maxInt = std.math.maxInt; | ... | @@ -6,14 +6,6 @@ const maxInt = std.math.maxInt; |
| 6 | const minInt = std.math.minInt; | 6 | const minInt = std.math.minInt; |
| 7 | const mem = std.mem; | 7 | const mem = std.mem; |
| 8 | 8 | ||
| 9 | test "@mulWithOverflow" { | ||
| 10 | var result: u8 = undefined; | ||
| 11 | try expect(@mulWithOverflow(u8, 86, 3, &result)); | ||
| 12 | try expect(result == 2); | ||
| 13 | try expect(!@mulWithOverflow(u8, 85, 3, &result)); | ||
| 14 | try expect(result == 255); | ||
| 15 | } | ||
| 16 | |||
| 17 | test "@subWithOverflow" { | 9 | test "@subWithOverflow" { |
| 18 | var result: u8 = undefined; | 10 | var result: u8 = undefined; |
| 19 | try expect(@subWithOverflow(u8, 1, 2, &result)); | 11 | try expect(@subWithOverflow(u8, 1, 2, &result)); |