authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-12-20 02:06:09+01:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-12-21 01:41:51+01:00
logc47ed0c912d2f445710fe4486fa071dd63601989
tree3272549828e1ca5fbb65b43a6cad1ea6e200cbd4
parentddd2ef822f99979d3ea61583a91ab236942e6367

stage2: @mulWithOverflow


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 },
817823
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}
821829
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 stored7348 // 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 }
73807395
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 };
73897437
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),
523523
524 .add_with_overflow => try self.airAddWithOverflow(inst),524 .add_with_overflow => try self.airAddWithOverflow(inst),
525 .mul_with_overflow => try self.airMulWithOverflow(inst),
525526
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),
527528
...@@ -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}
978979
980fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
981 _ = inst;
982 return self.fail("TODO implement airMulResultWithOverflow for {}", .{self.target.cpu.arch});
983}
984
979fn airDiv(self: *Self, inst: Air.Inst.Index) !void {985fn 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),
521521
522 .add_with_overflow => try self.airAddWithOverflow(inst),522 .add_with_overflow => try self.airAddWithOverflow(inst),
523 .mul_with_overflow => try self.airMulWithOverflow(inst),
523524
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),
525526
...@@ -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}
10081009
1010fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1011 _ = inst;
1012 return self.fail("TODO implement airMulResultWithOverflow for {}", .{self.target.cpu.arch});
1013}
1014
1009fn airDiv(self: *Self, inst: Air.Inst.Index) !void {1015fn 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),
502502
503 .add_with_overflow => try self.airAddWithOverflow(inst),503 .add_with_overflow => try self.airAddWithOverflow(inst),
504 .mul_with_overflow => try self.airMulWithOverflow(inst),
504505
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),
506507
...@@ -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}
923924
925fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
926 _ = inst;
927 return self.fail("TODO implement airMulResultWithOverflow for {}", .{self.target.cpu.arch});
928}
929
924fn airDiv(self: *Self, inst: Air.Inst.Index) !void {930fn 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),
555555
556 .add_with_overflow => try self.airAddWithOverflow(inst),556 .add_with_overflow => try self.airAddWithOverflow(inst),
557 .mul_with_overflow => try self.airMulWithOverflow(inst),
557558
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),
559560
...@@ -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}
10371038
1039fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1040 _ = inst;
1041 return self.fail("TODO implement airMulResultWithOverflow for {}", .{self.target.cpu.arch});
1042}
1043
1038fn airDiv(self: *Self, inst: Air.Inst.Index) !void {1044fn 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_"),
11581158
1159 .add_with_overflow => try airAddWithOverflow(f, inst),1159 .add_with_overflow => try airAddWithOverflow(f, inst),
1160 .mul_with_overflow => try airMulWithOverflow(f, inst),
11601161
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}
18751876
1877fn airMulWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue {
1878 _ = f;
1879 _ = inst;
1880 return f.fail("TODO mul with overflow", .{});
1881}
1882
1876fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {1883fn 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),
17161716
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"),
17181719
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 }
31383139
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;
31423148
...@@ -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);
31523158
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";
31573160
3158 const llvm_lhs_ty = try self.dg.llvmType(lhs_ty);3161 const llvm_lhs_ty = try self.dg.llvmType(lhs_ty);
31593162
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 }
235238
...@@ -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 }
352355
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;
356359
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 }
21322132
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);
21482141
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 }
21662185
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}
455463
456test "small int addition" {464test "small int addition" {
...@@ -471,3 +479,19 @@ test "small int addition" {...@@ -471,3 +479,19 @@ test "small int addition" {
471479
472 try expect(result == 0);480 try expect(result == 0);
473}481}
482
483test "@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;
6const minInt = std.math.minInt;6const minInt = std.math.minInt;
7const mem = std.mem;7const mem = std.mem;
88
9test "@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
17test "@subWithOverflow" {9test "@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));