authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-12-18 04:42:13+01:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-12-21 01:41:51+01:00
logf3d635b6683ba4a53f82ae8087b1cf78552abac5
treebd038a7404df4e79170915e07528694768f44f7c
parent28bcd7dbdda7fb2c2fe80dbdb5981479a04e973a

stage2: @addWithOverflow


15 files changed, 311 insertions(+), 82 deletions(-)

lib/std/math/big/int.zig+23-13
...@@ -443,12 +443,12 @@ pub const Mutable = struct {...@@ -443,12 +443,12 @@ pub const Mutable = struct {
443 }443 }
444 }444 }
445445
446 /// r = a + b with 2s-complement wrapping semantics.446 /// r = a + b with 2s-complement wrapping semantics. Returns whether overflow occurred.
447 /// r, a and b may be aliases447 /// r, a and b may be aliases
448 ///448 ///
449 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by449 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
450 /// r is `calcTwosCompLimbCount(bit_count)`.450 /// r is `calcTwosCompLimbCount(bit_count)`.
451 pub fn addWrap(r: *Mutable, a: Const, b: Const, signedness: Signedness, bit_count: usize) void {451 pub fn addWrap(r: *Mutable, a: Const, b: Const, signedness: Signedness, bit_count: usize) bool {
452 const req_limbs = calcTwosCompLimbCount(bit_count);452 const req_limbs = calcTwosCompLimbCount(bit_count);
453453
454 // Slice of the upper bits if they exist, these will be ignored and allows us to use addCarry to determine454 // Slice of the upper bits if they exist, these will be ignored and allows us to use addCarry to determine
...@@ -463,6 +463,7 @@ pub const Mutable = struct {...@@ -463,6 +463,7 @@ pub const Mutable = struct {
463 .limbs = b.limbs[0..math.min(req_limbs, b.limbs.len)],463 .limbs = b.limbs[0..math.min(req_limbs, b.limbs.len)],
464 };464 };
465465
466 var carry_truncated = false;
466 if (r.addCarry(x, y)) {467 if (r.addCarry(x, y)) {
467 // There are two possibilities here:468 // There are two possibilities here:
468 // - We overflowed req_limbs. In this case, the carry is ignored, as it would be removed by469 // - We overflowed req_limbs. In this case, the carry is ignored, as it would be removed by
...@@ -473,10 +474,17 @@ pub const Mutable = struct {...@@ -473,10 +474,17 @@ pub const Mutable = struct {
473 if (msl < req_limbs) {474 if (msl < req_limbs) {
474 r.limbs[msl] = 1;475 r.limbs[msl] = 1;
475 r.len = req_limbs;476 r.len = req_limbs;
477 } else {
478 carry_truncated = true;
476 }479 }
477 }480 }
478481
479 r.truncate(r.toConst(), signedness, bit_count);482 if (!r.toConst().fitsInTwosComp(signedness, bit_count)) {
483 r.truncate(r.toConst(), signedness, bit_count);
484 return true;
485 }
486
487 return carry_truncated;
480 }488 }
481489
482 /// r = a + b with 2s-complement saturating semantics.490 /// r = a + b with 2s-complement saturating semantics.
...@@ -581,13 +589,13 @@ pub const Mutable = struct {...@@ -581,13 +589,13 @@ pub const Mutable = struct {
581 r.add(a, b.negate());589 r.add(a, b.negate());
582 }590 }
583591
584 /// r = a - b with 2s-complement wrapping semantics.592 /// r = a - b with 2s-complement wrapping semantics. Returns whether any overflow occured.
585 ///593 ///
586 /// r, a and b may be aliases594 /// r, a and b may be aliases
587 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by595 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
588 /// r is `calcTwosCompLimbCount(bit_count)`.596 /// r is `calcTwosCompLimbCount(bit_count)`.
589 pub fn subWrap(r: *Mutable, a: Const, b: Const, signedness: Signedness, bit_count: usize) void {597 pub fn subWrap(r: *Mutable, a: Const, b: Const, signedness: Signedness, bit_count: usize) bool {
590 r.addWrap(a, b.negate(), signedness, bit_count);598 return r.addWrap(a, b.negate(), signedness, bit_count);
591 }599 }
592600
593 /// r = a - b with 2s-complement saturating semantics.601 /// r = a - b with 2s-complement saturating semantics.
...@@ -1039,7 +1047,7 @@ pub const Mutable = struct {...@@ -1039,7 +1047,7 @@ pub const Mutable = struct {
1039 pub fn bitNotWrap(r: *Mutable, a: Const, signedness: Signedness, bit_count: usize) void {1047 pub fn bitNotWrap(r: *Mutable, a: Const, signedness: Signedness, bit_count: usize) void {
1040 r.copy(a.negate());1048 r.copy(a.negate());
1041 const negative_one = Const{ .limbs = &.{1}, .positive = false };1049 const negative_one = Const{ .limbs = &.{1}, .positive = false };
1042 r.addWrap(r.toConst(), negative_one, signedness, bit_count);1050 _ = r.addWrap(r.toConst(), negative_one, signedness, bit_count);
1043 }1051 }
10441052
1045 /// r = a | b under 2s complement semantics.1053 /// r = a | b under 2s complement semantics.
...@@ -2443,17 +2451,18 @@ pub const Managed = struct {...@@ -2443,17 +2451,18 @@ pub const Managed = struct {
2443 r.setMetadata(m.positive, m.len);2451 r.setMetadata(m.positive, m.len);
2444 }2452 }
24452453
2446 /// r = a + b with 2s-complement wrapping semantics.2454 /// r = a + b with 2s-complement wrapping semantics. Returns whether any overflow occured.
2447 ///2455 ///
2448 /// r, a and b may be aliases. If r aliases a or b, then caller must call2456 /// r, a and b may be aliases. If r aliases a or b, then caller must call
2449 /// `r.ensureTwosCompCapacity` prior to calling `add`.2457 /// `r.ensureTwosCompCapacity` prior to calling `add`.
2450 ///2458 ///
2451 /// Returns an error if memory could not be allocated.2459 /// Returns an error if memory could not be allocated.
2452 pub fn addWrap(r: *Managed, a: Const, b: Const, signedness: Signedness, bit_count: usize) Allocator.Error!void {2460 pub fn addWrap(r: *Managed, a: Const, b: Const, signedness: Signedness, bit_count: usize) Allocator.Error!bool {
2453 try r.ensureTwosCompCapacity(bit_count);2461 try r.ensureTwosCompCapacity(bit_count);
2454 var m = r.toMutable();2462 var m = r.toMutable();
2455 m.addWrap(a, b, signedness, bit_count);2463 const wrapped = m.addWrap(a, b, signedness, bit_count);
2456 r.setMetadata(m.positive, m.len);2464 r.setMetadata(m.positive, m.len);
2465 return wrapped;
2457 }2466 }
24582467
2459 /// r = a + b with 2s-complement saturating semantics.2468 /// r = a + b with 2s-complement saturating semantics.
...@@ -2481,17 +2490,18 @@ pub const Managed = struct {...@@ -2481,17 +2490,18 @@ pub const Managed = struct {
2481 r.setMetadata(m.positive, m.len);2490 r.setMetadata(m.positive, m.len);
2482 }2491 }
24832492
2484 /// r = a - b with 2s-complement wrapping semantics.2493 /// r = a - b with 2s-complement wrapping semantics. Returns whether any overflow occured.
2485 ///2494 ///
2486 /// r, a and b may be aliases. If r aliases a or b, then caller must call2495 /// r, a and b may be aliases. If r aliases a or b, then caller must call
2487 /// `r.ensureTwosCompCapacity` prior to calling `add`.2496 /// `r.ensureTwosCompCapacity` prior to calling `add`.
2488 ///2497 ///
2489 /// Returns an error if memory could not be allocated.2498 /// Returns an error if memory could not be allocated.
2490 pub fn subWrap(r: *Managed, a: Const, b: Const, signedness: Signedness, bit_count: usize) Allocator.Error!void {2499 pub fn subWrap(r: *Managed, a: Const, b: Const, signedness: Signedness, bit_count: usize) Allocator.Error!bool {
2491 try r.ensureTwosCompCapacity(bit_count);2500 try r.ensureTwosCompCapacity(bit_count);
2492 var m = r.toMutable();2501 var m = r.toMutable();
2493 m.subWrap(a, b, signedness, bit_count);2502 const wrapped = m.subWrap(a, b, signedness, bit_count);
2494 r.setMetadata(m.positive, m.len);2503 r.setMetadata(m.positive, m.len);
2504 return wrapped;
2495 }2505 }
24962506
2497 /// r = a - b with 2s-complement saturating semantics.2507 /// r = a - b with 2s-complement saturating semantics.
lib/std/math/big/int_test.zig+16-8
...@@ -590,8 +590,9 @@ test "big.int addWrap single-single, unsigned" {...@@ -590,8 +590,9 @@ test "big.int addWrap single-single, unsigned" {
590 var b = try Managed.initSet(testing.allocator, 10);590 var b = try Managed.initSet(testing.allocator, 10);
591 defer b.deinit();591 defer b.deinit();
592592
593 try a.addWrap(a.toConst(), b.toConst(), .unsigned, 17);593 const wrapped = try a.addWrap(a.toConst(), b.toConst(), .unsigned, 17);
594594
595 try testing.expect(wrapped);
595 try testing.expect((try a.to(u17)) == 9);596 try testing.expect((try a.to(u17)) == 9);
596}597}
597598
...@@ -602,8 +603,9 @@ test "big.int subWrap single-single, unsigned" {...@@ -602,8 +603,9 @@ test "big.int subWrap single-single, unsigned" {
602 var b = try Managed.initSet(testing.allocator, maxInt(u17));603 var b = try Managed.initSet(testing.allocator, maxInt(u17));
603 defer b.deinit();604 defer b.deinit();
604605
605 try a.subWrap(a.toConst(), b.toConst(), .unsigned, 17);606 const wrapped = try a.subWrap(a.toConst(), b.toConst(), .unsigned, 17);
606607
608 try testing.expect(wrapped);
607 try testing.expect((try a.to(u17)) == 1);609 try testing.expect((try a.to(u17)) == 1);
608}610}
609611
...@@ -614,8 +616,9 @@ test "big.int addWrap multi-multi, unsigned, limb aligned" {...@@ -614,8 +616,9 @@ test "big.int addWrap multi-multi, unsigned, limb aligned" {
614 var b = try Managed.initSet(testing.allocator, maxInt(DoubleLimb));616 var b = try Managed.initSet(testing.allocator, maxInt(DoubleLimb));
615 defer b.deinit();617 defer b.deinit();
616618
617 try a.addWrap(a.toConst(), b.toConst(), .unsigned, @bitSizeOf(DoubleLimb));619 const wrapped = try a.addWrap(a.toConst(), b.toConst(), .unsigned, @bitSizeOf(DoubleLimb));
618620
621 try testing.expect(wrapped);
619 try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb) - 1);622 try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb) - 1);
620}623}
621624
...@@ -626,8 +629,9 @@ test "big.int subWrap single-multi, unsigned, limb aligned" {...@@ -626,8 +629,9 @@ test "big.int subWrap single-multi, unsigned, limb aligned" {
626 var b = try Managed.initSet(testing.allocator, maxInt(DoubleLimb) + 100);629 var b = try Managed.initSet(testing.allocator, maxInt(DoubleLimb) + 100);
627 defer b.deinit();630 defer b.deinit();
628631
629 try a.subWrap(a.toConst(), b.toConst(), .unsigned, @bitSizeOf(DoubleLimb));632 const wrapped = try a.subWrap(a.toConst(), b.toConst(), .unsigned, @bitSizeOf(DoubleLimb));
630633
634 try testing.expect(wrapped);
631 try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb) - 88);635 try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb) - 88);
632}636}
633637
...@@ -638,8 +642,9 @@ test "big.int addWrap single-single, signed" {...@@ -638,8 +642,9 @@ test "big.int addWrap single-single, signed" {
638 var b = try Managed.initSet(testing.allocator, 1 + 1 + maxInt(u21));642 var b = try Managed.initSet(testing.allocator, 1 + 1 + maxInt(u21));
639 defer b.deinit();643 defer b.deinit();
640644
641 try a.addWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(i21));645 const wrapped = try a.addWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(i21));
642646
647 try testing.expect(wrapped);
643 try testing.expect((try a.to(i21)) == minInt(i21));648 try testing.expect((try a.to(i21)) == minInt(i21));
644}649}
645650
...@@ -650,8 +655,9 @@ test "big.int subWrap single-single, signed" {...@@ -650,8 +655,9 @@ test "big.int subWrap single-single, signed" {
650 var b = try Managed.initSet(testing.allocator, 1);655 var b = try Managed.initSet(testing.allocator, 1);
651 defer b.deinit();656 defer b.deinit();
652657
653 try a.subWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(i21));658 const wrapped = try a.subWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(i21));
654659
660 try testing.expect(wrapped);
655 try testing.expect((try a.to(i21)) == maxInt(i21));661 try testing.expect((try a.to(i21)) == maxInt(i21));
656}662}
657663
...@@ -662,8 +668,9 @@ test "big.int addWrap multi-multi, signed, limb aligned" {...@@ -662,8 +668,9 @@ test "big.int addWrap multi-multi, signed, limb aligned" {
662 var b = try Managed.initSet(testing.allocator, maxInt(SignedDoubleLimb));668 var b = try Managed.initSet(testing.allocator, maxInt(SignedDoubleLimb));
663 defer b.deinit();669 defer b.deinit();
664670
665 try a.addWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(SignedDoubleLimb));671 const wrapped = try a.addWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(SignedDoubleLimb));
666672
673 try testing.expect(wrapped);
667 try testing.expect((try a.to(SignedDoubleLimb)) == -2);674 try testing.expect((try a.to(SignedDoubleLimb)) == -2);
668}675}
669676
...@@ -674,8 +681,9 @@ test "big.int subWrap single-multi, signed, limb aligned" {...@@ -674,8 +681,9 @@ test "big.int subWrap single-multi, signed, limb aligned" {
674 var b = try Managed.initSet(testing.allocator, 1);681 var b = try Managed.initSet(testing.allocator, 1);
675 defer b.deinit();682 defer b.deinit();
676683
677 try a.subWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(SignedDoubleLimb));684 const wrapped = try a.subWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(SignedDoubleLimb));
678685
686 try testing.expect(wrapped);
679 try testing.expect((try a.to(SignedDoubleLimb)) == maxInt(SignedDoubleLimb));687 try testing.expect((try a.to(SignedDoubleLimb)) == maxInt(SignedDoubleLimb));
680}688}
681689
src/Air.zig+8
...@@ -135,6 +135,12 @@ pub const Inst = struct {...@@ -135,6 +135,12 @@ pub const Inst = struct {
135 /// is the same as both operands.135 /// is the same as both operands.
136 /// Uses the `bin_op` field.136 /// Uses the `bin_op` field.
137 min,137 min,
138 /// Integer addition with overflow. Both operands are guaranteed to be the same type,
139 /// and the result is bool. The wrapped value is written to the pointer given by the in
140 /// operand of the `pl_op` field. Payload is `Bin` with `lhs` and `rhs` the relevant types
141 /// of the operation.
142 /// Uses the `pl_op` field with payload `Bin`.
143 add_with_overflow,
138 /// Allocates stack local memory.144 /// Allocates stack local memory.
139 /// Uses the `ty` field.145 /// Uses the `ty` field.
140 alloc,146 alloc,
...@@ -804,6 +810,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -804,6 +810,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
804 const ptr_ty = air.typeOf(datas[inst].pl_op.operand);810 const ptr_ty = air.typeOf(datas[inst].pl_op.operand);
805 return ptr_ty.elemType();811 return ptr_ty.elemType();
806 },812 },
813
814 .add_with_overflow => return Type.initTag(.bool),
807 }815 }
808}816}
809817
src/Liveness.zig+1-1
...@@ -381,7 +381,7 @@ fn analyzeInst(...@@ -381,7 +381,7 @@ fn analyzeInst(
381 const extra = a.air.extraData(Air.AtomicRmw, pl_op.payload).data;381 const extra = a.air.extraData(Air.AtomicRmw, pl_op.payload).data;
382 return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.operand, .none });382 return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.operand, .none });
383 },383 },
384 .memset, .memcpy => {384 .memset, .memcpy, .add_with_overflow => {
385 const pl_op = inst_datas[inst].pl_op;385 const pl_op = inst_datas[inst].pl_op;
386 const extra = a.air.extraData(Air.Bin, pl_op.payload).data;386 const extra = a.air.extraData(Air.Bin, pl_op.payload).data;
387 return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.lhs, extra.rhs });387 return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.lhs, extra.rhs });
src/Sema.zig+94-5
...@@ -1051,10 +1051,10 @@ fn zirExtended(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -1051,10 +1051,10 @@ fn zirExtended(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1051 .@"asm" => return sema.zirAsm( block, extended, inst),1051 .@"asm" => return sema.zirAsm( block, extended, inst),
1052 .typeof_peer => return sema.zirTypeofPeer( block, extended),1052 .typeof_peer => return sema.zirTypeofPeer( block, extended),
1053 .compile_log => return sema.zirCompileLog( block, extended),1053 .compile_log => return sema.zirCompileLog( block, extended),
1054 .add_with_overflow => return sema.zirOverflowArithmetic(block, extended),1054 .add_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode),
1055 .sub_with_overflow => return sema.zirOverflowArithmetic(block, extended),1055 .sub_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode),
1056 .mul_with_overflow => return sema.zirOverflowArithmetic(block, extended),1056 .mul_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode),
1057 .shl_with_overflow => return sema.zirOverflowArithmetic(block, extended),1057 .shl_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode),
1058 .c_undef => return sema.zirCUndef( block, extended),1058 .c_undef => return sema.zirCUndef( block, extended),
1059 .c_include => return sema.zirCInclude( block, extended),1059 .c_include => return sema.zirCInclude( block, extended),
1060 .c_define => return sema.zirCDefine( block, extended),1060 .c_define => return sema.zirCDefine( block, extended),
...@@ -7310,6 +7310,7 @@ fn zirOverflowArithmetic(...@@ -7310,6 +7310,7 @@ fn zirOverflowArithmetic(
7310 sema: *Sema,7310 sema: *Sema,
7311 block: *Block,7311 block: *Block,
7312 extended: Zir.Inst.Extended.InstData,7312 extended: Zir.Inst.Extended.InstData,
7313 zir_tag: Zir.Inst.Extended,
7313) CompileError!Air.Inst.Ref {7314) CompileError!Air.Inst.Ref {
7314 const tracy = trace(@src());7315 const tracy = trace(@src());
7315 defer tracy.end();7316 defer tracy.end();
...@@ -7317,7 +7318,95 @@ fn zirOverflowArithmetic(...@@ -7317,7 +7318,95 @@ fn zirOverflowArithmetic(
7317 const extra = sema.code.extraData(Zir.Inst.OverflowArithmetic, extended.operand).data;7318 const extra = sema.code.extraData(Zir.Inst.OverflowArithmetic, extended.operand).data;
7318 const src: LazySrcLoc = .{ .node_offset = extra.node };7319 const src: LazySrcLoc = .{ .node_offset = extra.node };
73197320
7320 return sema.fail(block, src, "TODO implement Sema.zirOverflowArithmetic", .{});7321 const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
7322 const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
7323 const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = extra.node };
7324
7325 const lhs = sema.resolveInst(extra.lhs);
7326 const rhs = sema.resolveInst(extra.rhs);
7327 const ptr = sema.resolveInst(extra.ptr);
7328
7329 const lhs_ty = sema.typeOf(lhs);
7330
7331 // Note, the types of lhs/rhs (also for shifting)/ptr are already correct as ensured by astgen.
7332 const dest_ty = lhs_ty;
7333 if (dest_ty.zigTypeTag() != .Int) {
7334 return sema.fail(block, src, "expected integer type, found '{}'", .{dest_ty});
7335 }
7336
7337 const target = sema.mod.getTarget();
7338
7339 const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);
7340 const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);
7341
7342 const result: struct {
7343 overflowed: enum { yes, no, undef },
7344 wrapped: Air.Inst.Ref,
7345 } = result: {
7346 const air_tag: Air.Inst.Tag = switch (zir_tag) {
7347 .add_with_overflow => blk: {
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..
7350 // Otherwise, if either of the argument is undefined, undefined is returned.
7351 if (maybe_lhs_val) |lhs_val| {
7352 if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
7353 break :result .{ .overflowed = .no, .wrapped = rhs };
7354 }
7355 }
7356 if (maybe_rhs_val) |rhs_val| {
7357 if (!rhs_val.isUndef() and rhs_val.compareWithZero(.eq)) {
7358 break :result .{ .overflowed = .no, .wrapped = lhs };
7359 }
7360 }
7361 if (maybe_lhs_val) |lhs_val| {
7362 if (maybe_rhs_val) |rhs_val| {
7363 if (lhs_val.isUndef() or rhs_val.isUndef()) {
7364 break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) };
7365 }
7366
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 };
7377 }
7378 }
7379 }
7380
7381 break :blk .add_with_overflow;
7382 },
7383 .sub_with_overflow,
7384 .mul_with_overflow,
7385 .shl_with_overflow,
7386 => return sema.fail(block, src, "TODO implement Sema.zirOverflowArithmetic for {}", .{zir_tag}),
7387 else => unreachable,
7388 };
7389
7390 try sema.requireRuntimeBlock(block, src);
7391 return block.addInst(.{
7392 .tag = air_tag,
7393 .data = .{ .pl_op = .{
7394 .operand = ptr,
7395 .payload = try sema.addExtra(Air.Bin{
7396 .lhs = lhs,
7397 .rhs = rhs,
7398 }),
7399 } },
7400 });
7401 };
7402
7403 try sema.storePtr2(block, src, ptr, ptr_src, result.wrapped, src, .store);
7404
7405 return switch (result.overflowed) {
7406 .yes => Air.Inst.Ref.bool_true,
7407 .no => Air.Inst.Ref.bool_false,
7408 .undef => try sema.addConstUndef(Type.initTag(.bool)),
7409 };
7321}7410}
73227411
7323fn analyzeArithmetic(7412fn analyzeArithmetic(
src/arch/aarch64/CodeGen.zig+7
...@@ -521,6 +521,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -521,6 +521,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
521 .max => try self.airMax(inst),521 .max => try self.airMax(inst),
522 .slice => try self.airSlice(inst),522 .slice => try self.airSlice(inst),
523523
524 .add_with_overflow => try self.airAddWithOverflow(inst),
525
524 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),526 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
525527
526 .cmp_lt => try self.airCmp(inst, .lt),528 .cmp_lt => try self.airCmp(inst, .lt),
...@@ -968,6 +970,11 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {...@@ -968,6 +970,11 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
968 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });970 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
969}971}
970972
973fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
974 _ = inst;
975 return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch});
976}
977
971fn airDiv(self: *Self, inst: Air.Inst.Index) !void {978fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
972 const bin_op = self.air.instructions.items(.data)[inst].bin_op;979 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
973 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch});980 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+7
...@@ -519,6 +519,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -519,6 +519,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
519 .max => try self.airMax(inst),519 .max => try self.airMax(inst),
520 .slice => try self.airSlice(inst),520 .slice => try self.airSlice(inst),
521521
522 .add_with_overflow => try self.airAddWithOverflow(inst),
523
522 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),524 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
523525
524 .cmp_lt => try self.airCmp(inst, .lt),526 .cmp_lt => try self.airCmp(inst, .lt),
...@@ -998,6 +1000,11 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {...@@ -998,6 +1000,11 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
998 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1000 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
999}1001}
10001002
1003fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1004 _ = inst;
1005 return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch});
1006}
1007
1001fn airDiv(self: *Self, inst: Air.Inst.Index) !void {1008fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
1002 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1009 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1003 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch});1010 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+7
...@@ -500,6 +500,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -500,6 +500,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
500 .max => try self.airMax(inst),500 .max => try self.airMax(inst),
501 .slice => try self.airSlice(inst),501 .slice => try self.airSlice(inst),
502502
503 .add_with_overflow => try self.airAddWithOverflow(inst),
504
503 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),505 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
504506
505 .cmp_lt => try self.airCmp(inst, .lt),507 .cmp_lt => try self.airCmp(inst, .lt),
...@@ -913,6 +915,11 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {...@@ -913,6 +915,11 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
913 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });915 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
914}916}
915917
918fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
919 _ = inst;
920 return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch});
921}
922
916fn airDiv(self: *Self, inst: Air.Inst.Index) !void {923fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
917 const bin_op = self.air.instructions.items(.data)[inst].bin_op;924 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
918 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch});925 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+7
...@@ -553,6 +553,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -553,6 +553,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
553 .max => try self.airMax(inst),553 .max => try self.airMax(inst),
554 .slice => try self.airSlice(inst),554 .slice => try self.airSlice(inst),
555555
556 .add_with_overflow => try self.airAddWithOverflow(inst),
557
556 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),558 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
557559
558 .cmp_lt => try self.airCmp(inst, .lt),560 .cmp_lt => try self.airCmp(inst, .lt),
...@@ -1027,6 +1029,11 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {...@@ -1027,6 +1029,11 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
1027 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1029 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1028}1030}
10291031
1032fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1033 _ = inst;
1034 return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch});
1035}
1036
1030fn airDiv(self: *Self, inst: Air.Inst.Index) !void {1037fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
1031 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1038 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1032 const result: MCValue = if (self.liveness.isUnused(inst))1039 const result: MCValue = if (self.liveness.isUnused(inst))
src/codegen/c.zig+8
...@@ -1155,6 +1155,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1155,6 +1155,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
1155 .mul_sat => try airSatOp(f, inst, "muls_"),1155 .mul_sat => try airSatOp(f, inst, "muls_"),
1156 .shl_sat => try airSatOp(f, inst, "shls_"),1156 .shl_sat => try airSatOp(f, inst, "shls_"),
11571157
1158 .add_with_overflow => try airAddWithOverflow(f, inst),
1159
1158 .min => try airMinMax(f, inst, "<"),1160 .min => try airMinMax(f, inst, "<"),
1159 .max => try airMinMax(f, inst, ">"),1161 .max => try airMinMax(f, inst, ">"),
11601162
...@@ -1864,6 +1866,12 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue {...@@ -1864,6 +1866,12 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue {
1864 return ret;1866 return ret;
1865}1867}
18661868
1869fn airAddWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue {
1870 _ = f;
1871 _ = inst;
1872 return f.fail("TODO add with overflow", .{});
1873}
1874
1867fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {1875fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {
1868 if (f.liveness.isUnused(inst))1876 if (f.liveness.isUnused(inst))
1869 return CValue.none;1877 return CValue.none;
src/codegen/llvm.zig+37-6
...@@ -1714,6 +1714,8 @@ pub const FuncGen = struct {...@@ -1714,6 +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),
1718
1717 .bit_and, .bool_and => try self.airAnd(inst),1719 .bit_and, .bool_and => try self.airAnd(inst),
1718 .bit_or, .bool_or => try self.airOr(inst),1720 .bit_or, .bool_or => try self.airOr(inst),
1719 .xor => try self.airXor(inst),1721 .xor => try self.airXor(inst),
...@@ -3133,6 +3135,38 @@ pub const FuncGen = struct {...@@ -3133,6 +3135,38 @@ pub const FuncGen = struct {
3133 }3135 }
3134 }3136 }
31353137
3138 fn airAddWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
3139 if (self.liveness.isUnused(inst))
3140 return null;
3141
3142 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3143 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
3144
3145 const ptr = try self.resolveInst(pl_op.operand);
3146 const lhs = try self.resolveInst(extra.lhs);
3147 const rhs = try self.resolveInst(extra.rhs);
3148
3149 const ptr_ty = self.air.typeOf(pl_op.operand);
3150 const lhs_ty = self.air.typeOf(extra.lhs);
3151
3152 const intrinsic_name: []const u8 = if (lhs_ty.isSignedInt())
3153 "llvm.sadd.with.overflow"
3154 else
3155 "llvm.uadd.with.overflow";
3156
3157 const llvm_lhs_ty = try self.dg.llvmType(lhs_ty);
3158
3159 const llvm_fn = self.getIntrinsic(intrinsic_name, &.{llvm_lhs_ty});
3160 const result_struct = self.builder.buildCall(llvm_fn, &[_]*const llvm.Value{ lhs, rhs }, 2, .Fast, .Auto, "");
3161
3162 const result = self.builder.buildExtractValue(result_struct, 0, "");
3163 const overflow_bit = self.builder.buildExtractValue(result_struct, 1, "");
3164
3165 self.store(ptr, ptr_ty, result, .NotAtomic);
3166
3167 return overflow_bit;
3168 }
3169
3136 fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {3170 fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
3137 if (self.liveness.isUnused(inst))3171 if (self.liveness.isUnused(inst))
3138 return null;3172 return null;
...@@ -3511,7 +3545,7 @@ pub const FuncGen = struct {...@@ -3511,7 +3545,7 @@ pub const FuncGen = struct {
35113545
3512 fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {3546 fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
3513 _ = inst;3547 _ = inst;
3514 const llvm_fn = self.getIntrinsic("llvm.debugtrap");3548 const llvm_fn = self.getIntrinsic("llvm.debugtrap", &.{});
3515 _ = self.builder.buildCall(llvm_fn, undefined, 0, .C, .Auto, "");3549 _ = self.builder.buildCall(llvm_fn, undefined, 0, .C, .Auto, "");
3516 return null;3550 return null;
3517 }3551 }
...@@ -3946,13 +3980,10 @@ pub const FuncGen = struct {...@@ -3946,13 +3980,10 @@ pub const FuncGen = struct {
3946 return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");3980 return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
3947 }3981 }
39483982
3949 fn getIntrinsic(self: *FuncGen, name: []const u8) *const llvm.Value {3983 fn getIntrinsic(self: *FuncGen, name: []const u8, types: []*const llvm.Type) *const llvm.Value {
3950 const id = llvm.lookupIntrinsicID(name.ptr, name.len);3984 const id = llvm.lookupIntrinsicID(name.ptr, name.len);
3951 assert(id != 0);3985 assert(id != 0);
3952 // TODO: add support for overload intrinsics by passing the prefix of the intrinsic3986 return self.llvmModule().getIntrinsicDeclaration(id, types.ptr, types.len);
3953 // to `lookupIntrinsicID` and then passing the correct types to
3954 // `getIntrinsicDeclaration`
3955 return self.llvmModule().getIntrinsicDeclaration(id, null, 0);
3956 }3987 }
39573988
3958 fn load(self: *FuncGen, ptr: *const llvm.Value, ptr_ty: Type) ?*const llvm.Value {3989 fn load(self: *FuncGen, ptr: *const llvm.Value, ptr_ty: Type) ?*const llvm.Value {
src/print_air.zig+12
...@@ -228,6 +228,7 @@ const Writer = struct {...@@ -228,6 +228,7 @@ const Writer = struct {
228 .atomic_rmw => try w.writeAtomicRmw(s, inst),228 .atomic_rmw => try w.writeAtomicRmw(s, inst),
229 .memcpy => try w.writeMemcpy(s, inst),229 .memcpy => try w.writeMemcpy(s, inst),
230 .memset => try w.writeMemset(s, inst),230 .memset => try w.writeMemset(s, inst),
231 .add_with_overflow => try w.writeAddWithOverflow(s, inst),
231 }232 }
232 }233 }
233234
...@@ -348,6 +349,17 @@ const Writer = struct {...@@ -348,6 +349,17 @@ const Writer = struct {
348 try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) });349 try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) });
349 }350 }
350351
352 fn writeAddWithOverflow(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
353 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
354 const extra = w.air.extraData(Air.Bin, pl_op.payload).data;
355
356 try w.writeOperand(s, inst, 0, pl_op.operand);
357 try s.writeAll(", ");
358 try w.writeOperand(s, inst, 1, extra.lhs);
359 try s.writeAll(", ");
360 try w.writeOperand(s, inst, 2, extra.rhs);
361 }
362
351 fn writeMemset(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {363 fn writeMemset(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
352 const pl_op = w.air.instructions.items(.data)[inst].pl_op;364 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
353 const extra = w.air.extraData(Air.Bin, pl_op.payload).data;365 const extra = w.air.extraData(Air.Bin, pl_op.payload).data;
src/value.zig+57-22
...@@ -1969,20 +1969,18 @@ pub const Value = extern union {...@@ -1969,20 +1969,18 @@ pub const Value = extern union {
1969 return @divFloor(@floatToInt(std.math.big.Limb, std.math.log2(w_value)), @typeInfo(std.math.big.Limb).Int.bits) + 1;1969 return @divFloor(@floatToInt(std.math.big.Limb, std.math.log2(w_value)), @typeInfo(std.math.big.Limb).Int.bits) + 1;
1970 }1970 }
19711971
1972 /// Supports both floats and ints; handles undefined.1972 pub const OverflowArithmeticResult = struct {
1973 pub fn numberAddWrap(1973 overflowed: bool,
1974 wrapped_result: Value,
1975 };
1976
1977 pub fn intAddWithOverflow(
1974 lhs: Value,1978 lhs: Value,
1975 rhs: Value,1979 rhs: Value,
1976 ty: Type,1980 ty: Type,
1977 arena: Allocator,1981 arena: Allocator,
1978 target: Target,1982 target: Target,
1979 ) !Value {1983 ) !OverflowArithmeticResult {
1980 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1981
1982 if (ty.isAnyFloat()) {
1983 return floatAdd(lhs, rhs, ty, arena);
1984 }
1985
1986 const info = ty.intInfo(target);1984 const info = ty.intInfo(target);
19871985
1988 var lhs_space: Value.BigIntSpace = undefined;1986 var lhs_space: Value.BigIntSpace = undefined;
...@@ -1994,8 +1992,30 @@ pub const Value = extern union {...@@ -1994,8 +1992,30 @@ pub const Value = extern union {
1994 std.math.big.int.calcTwosCompLimbCount(info.bits),1992 std.math.big.int.calcTwosCompLimbCount(info.bits),
1995 );1993 );
1996 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };1994 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1997 result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);1995 const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
1998 return fromBigInt(arena, result_bigint.toConst());1996 const result = try fromBigInt(arena, result_bigint.toConst());
1997 return OverflowArithmeticResult{
1998 .overflowed = overflowed,
1999 .wrapped_result = result,
2000 };
2001 }
2002
2003 /// Supports both floats and ints; handles undefined.
2004 pub fn numberAddWrap(
2005 lhs: Value,
2006 rhs: Value,
2007 ty: Type,
2008 arena: Allocator,
2009 target: Target,
2010 ) !Value {
2011 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
2012
2013 if (ty.isAnyFloat()) {
2014 return floatAdd(lhs, rhs, ty, arena);
2015 }
2016
2017 const overflow_result = try intAddWithOverflow(lhs, rhs, ty, arena, target);
2018 return overflow_result.wrapped_result;
1999 }2019 }
20002020
2001 fn fromBigInt(arena: Allocator, big_int: BigIntConst) !Value {2021 fn fromBigInt(arena: Allocator, big_int: BigIntConst) !Value {
...@@ -2040,20 +2060,13 @@ pub const Value = extern union {...@@ -2040,20 +2060,13 @@ pub const Value = extern union {
2040 return fromBigInt(arena, result_bigint.toConst());2060 return fromBigInt(arena, result_bigint.toConst());
2041 }2061 }
20422062
2043 /// Supports both floats and ints; handles undefined.2063 pub fn intSubWithOverflow(
2044 pub fn numberSubWrap(
2045 lhs: Value,2064 lhs: Value,
2046 rhs: Value,2065 rhs: Value,
2047 ty: Type,2066 ty: Type,
2048 arena: Allocator,2067 arena: Allocator,
2049 target: Target,2068 target: Target,
2050 ) !Value {2069 ) !OverflowArithmeticResult {
2051 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
2052
2053 if (ty.isAnyFloat()) {
2054 return floatSub(lhs, rhs, ty, arena);
2055 }
2056
2057 const info = ty.intInfo(target);2070 const info = ty.intInfo(target);
20582071
2059 var lhs_space: Value.BigIntSpace = undefined;2072 var lhs_space: Value.BigIntSpace = undefined;
...@@ -2065,8 +2078,30 @@ pub const Value = extern union {...@@ -2065,8 +2078,30 @@ pub const Value = extern union {
2065 std.math.big.int.calcTwosCompLimbCount(info.bits),2078 std.math.big.int.calcTwosCompLimbCount(info.bits),
2066 );2079 );
2067 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };2080 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2068 result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);2081 const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
2069 return fromBigInt(arena, result_bigint.toConst());2082 const wrapped_result = try fromBigInt(arena, result_bigint.toConst());
2083 return OverflowArithmeticResult{
2084 .overflowed = overflowed,
2085 .wrapped_result = wrapped_result,
2086 };
2087 }
2088
2089 /// Supports both floats and ints; handles undefined.
2090 pub fn numberSubWrap(
2091 lhs: Value,
2092 rhs: Value,
2093 ty: Type,
2094 arena: Allocator,
2095 target: Target,
2096 ) !Value {
2097 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
2098
2099 if (ty.isAnyFloat()) {
2100 return floatSub(lhs, rhs, ty, arena);
2101 }
2102
2103 const overflow_result = try intSubWithOverflow(lhs, rhs, ty, arena, target);
2104 return overflow_result.wrapped_result;
2070 }2105 }
20712106
2072 /// Supports integers only; asserts neither operand is undefined.2107 /// Supports integers only; asserts neither operand is undefined.
test/behavior/math.zig+27
...@@ -444,3 +444,30 @@ test "128-bit multiplication" {...@@ -444,3 +444,30 @@ test "128-bit multiplication" {
444 var c = a * b;444 var c = a * b;
445 try expect(c == 6);445 try expect(c == 6);
446}446}
447
448test "@addWithOverflow" {
449 var result: u8 = undefined;
450 try expect(@addWithOverflow(u8, 250, 100, &result));
451 try expect(result == 94);
452 try expect(!@addWithOverflow(u8, 100, 150, &result));
453 try expect(result == 250);
454}
455
456test "small int addition" {
457 var x: u2 = 0;
458 try expect(x == 0);
459
460 x += 1;
461 try expect(x == 1);
462
463 x += 1;
464 try expect(x == 2);
465
466 x += 1;
467 try expect(x == 3);
468
469 var result: @TypeOf(x) = 3;
470 try expect(@addWithOverflow(@TypeOf(x), x, 1, &result));
471
472 try expect(result == 0);
473}
test/behavior/math_stage1.zig-27
...@@ -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 "@addWithOverflow" {
10 var result: u8 = undefined;
11 try expect(@addWithOverflow(u8, 250, 100, &result));
12 try expect(result == 94);
13 try expect(!@addWithOverflow(u8, 100, 150, &result));
14 try expect(result == 250);
15}
16
17test "@mulWithOverflow" {9test "@mulWithOverflow" {
18 var result: u8 = undefined;10 var result: u8 = undefined;
19 try expect(@mulWithOverflow(u8, 86, 3, &result));11 try expect(@mulWithOverflow(u8, 86, 3, &result));
...@@ -90,25 +82,6 @@ fn testCtzVectors() !void {...@@ -90,25 +82,6 @@ fn testCtzVectors() !void {
90 try expectEqual(@ctz(u16, @splat(64, @as(u16, 0b00000000))), @splat(64, @as(u5, 16)));82 try expectEqual(@ctz(u16, @splat(64, @as(u16, 0b00000000))), @splat(64, @as(u5, 16)));
91}83}
9284
93test "small int addition" {
94 var x: u2 = 0;
95 try expect(x == 0);
96
97 x += 1;
98 try expect(x == 1);
99
100 x += 1;
101 try expect(x == 2);
102
103 x += 1;
104 try expect(x == 3);
105
106 var result: @TypeOf(x) = 3;
107 try expect(@addWithOverflow(@TypeOf(x), x, 1, &result));
108
109 try expect(result == 0);
110}
111
112test "allow signed integer division/remainder when values are comptime known and positive or exact" {85test "allow signed integer division/remainder when values are comptime known and positive or exact" {
113 try expect(5 / 3 == 1);86 try expect(5 / 3 == 1);
114 try expect(-5 / -3 == 1);87 try expect(-5 / -3 == 1);