Commit 622311fb9a

Veikka Tuominen <git@vexu.eu>
2022-12-21 15:40:30
update uses of overflow arithmetic builtins
1 parent 54160e7
doc/langref.html.in
@@ -5413,14 +5413,14 @@ pub fn parseU64(buf: []const u8, radix: u8) !u64 {
         }
 
         // x *= radix
-        if (@mulWithOverflow(u64, x, radix, &x)) {
-            return error.Overflow;
-        }
+        var ov = @mulWithOverflow(x, radix);
+        if (ov[1] != 0) return error.OverFlow;
+        
 
         // x += digit
-        if (@addWithOverflow(u64, x, digit, &x)) {
-            return error.Overflow;
-        }
+        ov = @addWithOverflow(ov[0], digit);
+        if (ov[1] != 0) return error.OverFlow;
+        x = ov[0];
     }
 
     return x;
@@ -5832,14 +5832,16 @@ test "merge error sets" {
 {#code_begin|test|inferred_error_sets#}
 // With an inferred error set
 pub fn add_inferred(comptime T: type, a: T, b: T) !T {
-    var answer: T = undefined;
-    return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer;
+    const ov = @addWithOverflow(a, b);
+    if (ov[1] != 0) return error.Overflow;
+    return ov[0];
 }
 
 // With an explicit error set
 pub fn add_explicit(comptime T: type, a: T, b: T) Error!T {
-    var answer: T = undefined;
-    return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer;
+    const ov = @addWithOverflow(a, b);
+    if (ov[1] != 0) return error.Overflow;
+    return ov[0];
 }
 
 const Error = error {
@@ -7632,11 +7634,9 @@ test "global assembly" {
       </p>
       {#header_close#}
       {#header_open|@addWithOverflow#}
-      <pre>{#syntax#}@addWithOverflow(comptime T: type, a: T, b: T, result: *T) bool{#endsyntax#}</pre>
+      <pre>{#syntax#}@addWithOverflow(a: anytype, b: anytype) struct { @TypeOf(a, b), u1 }{#endsyntax#}</pre>
       <p>
-      Performs {#syntax#}result.* = a + b{#endsyntax#}. If overflow or underflow occurs,
-          stores the overflowed bits in {#syntax#}result{#endsyntax#} and returns {#syntax#}true{#endsyntax#}.
-                  If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}.
+      Performs {#syntax#}a + b{#endsyntax#} and returns a tuple with the result and a possible overflow bit.
       </p>
       {#header_close#}
       {#header_open|@alignCast#}
@@ -8695,11 +8695,9 @@ test "@wasmMemoryGrow" {
       {#header_close#}
 
       {#header_open|@mulWithOverflow#}
-      <pre>{#syntax#}@mulWithOverflow(comptime T: type, a: T, b: T, result: *T) bool{#endsyntax#}</pre>
+      <pre>{#syntax#}@mulWithOverflow(a: anytype, b: anytype) struct { @TypeOf(a, b), u1 }{#endsyntax#}</pre>
       <p>
-      Performs {#syntax#}result.* = a * b{#endsyntax#}. If overflow or underflow occurs,
-          stores the overflowed bits in {#syntax#}result{#endsyntax#} and returns {#syntax#}true{#endsyntax#}.
-                  If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}.
+      Performs {#syntax#}a * b{#endsyntax#} and returns a tuple with the result and a possible overflow bit.
       </p>
       {#header_close#}
 
@@ -8973,15 +8971,13 @@ test "@setRuntimeSafety" {
       {#header_close#}
 
       {#header_open|@shlWithOverflow#}
-      <pre>{#syntax#}@shlWithOverflow(comptime T: type, a: T, shift_amt: Log2T, result: *T) bool{#endsyntax#}</pre>
+      <pre>{#syntax#}@shlWithOverflow(a: anytype, shift_amt: Log2T) struct { @TypeOf(a), u1 }{#endsyntax#}</pre>
       <p>
-      Performs {#syntax#}result.* = a << b{#endsyntax#}. If overflow or underflow occurs,
-                 stores the overflowed bits in {#syntax#}result{#endsyntax#} and returns {#syntax#}true{#endsyntax#}.
-                         If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}.
+      Performs {#syntax#}a << b{#endsyntax#} and returns a tuple with the result and a possible overflow bit.
       </p>
       <p>
-      The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(@typeInfo(T).Int.bits){#endsyntax#} bits.
-              This is because {#syntax#}shift_amt >= @typeInfo(T).Int.bits{#endsyntax#} is undefined behavior.
+      The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(@typeInfo(@TypeOf(a)).Int.bits){#endsyntax#} bits.
+              This is because {#syntax#}shift_amt >= @typeInfo(@TypeOf(a)).Int.bits{#endsyntax#} is undefined behavior.
       </p>
       {#see_also|@shlExact|@shrExact#}
       {#header_close#}
@@ -9323,11 +9319,9 @@ fn doTheTest() !void {
       {#header_close#}
 
       {#header_open|@subWithOverflow#}
-      <pre>{#syntax#}@subWithOverflow(comptime T: type, a: T, b: T, result: *T) bool{#endsyntax#}</pre>
+      <pre>{#syntax#}@subWithOverflow(a: anytype, b: anytype) struct { @TypeOf(a, b), u1 }{#endsyntax#}</pre>
       <p>
-      Performs {#syntax#}result.* = a - b{#endsyntax#}. If overflow or underflow occurs,
-          stores the overflowed bits in {#syntax#}result{#endsyntax#} and returns {#syntax#}true{#endsyntax#}.
-                  If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}.
+      Performs {#syntax#}a - b{#endsyntax#} and returns a tuple with the result and a possible overflow bit.
       </p>
       {#header_close#}
 
@@ -9774,11 +9768,11 @@ const print = @import("std").debug.print;
 pub fn main() void {
     var byte: u8 = 255;
 
-    var result: u8 = undefined;
-    if (@addWithOverflow(u8, byte, 10, &result)) {
-        print("overflowed result: {}\n", .{result});
+    const ov = @addWithOverflow(byte, 10);
+    if (ov[1] != 0) {
+        print("overflowed result: {}\n", .{ov[0]});
     } else {
-        print("result: {}\n", .{result});
+        print("result: {}\n", .{ov[0]});
     }
 }
       {#code_end#}
lib/compiler_rt/trunctfxf2.zig
@@ -49,14 +49,16 @@ pub fn __trunctfxf2(a: f128) callconv(.C) f80 {
         const round_bits = a_abs & round_mask;
         if (round_bits > halfway) {
             // Round to nearest
-            const carry = @boolToInt(@addWithOverflow(u64, res.fraction, 1, &res.fraction));
-            res.exp += carry;
-            res.fraction |= @as(u64, carry) << 63; // Restore integer bit after carry
+            const ov = @addWithOverflow(res.fraction, 1);
+            res.fraction = ov[0];
+            res.exp += ov[1];
+            res.fraction |= @as(u64, ov[1]) << 63; // Restore integer bit after carry
         } else if (round_bits == halfway) {
             // Ties to even
-            const carry = @boolToInt(@addWithOverflow(u64, res.fraction, res.fraction & 1, &res.fraction));
-            res.exp += carry;
-            res.fraction |= @as(u64, carry) << 63; // Restore integer bit after carry
+            const ov = @addWithOverflow(res.fraction, res.fraction & 1);
+            res.fraction = ov[0];
+            res.exp += ov[1];
+            res.fraction |= @as(u64, ov[1]) << 63; // Restore integer bit after carry
         }
         if (res.exp == 0) res.fraction &= ~@as(u64, integer_bit); // Remove integer bit for de-normals
     }
lib/std/compress/deflate/compressor_test.zig
@@ -172,9 +172,7 @@ test "deflate/inflate" {
     defer testing.allocator.free(large_data_chunk);
     // fill with random data
     for (large_data_chunk) |_, i| {
-        var mul: u8 = @truncate(u8, i);
-        _ = @mulWithOverflow(u8, mul, mul, &mul);
-        large_data_chunk[i] = mul;
+        large_data_chunk[i] = @truncate(u8, i) *% @truncate(u8, i);
     }
     try testToFromWithLimit(large_data_chunk, limits);
 }
lib/std/crypto/pcurves/p256/p256_64.zig
@@ -75,10 +75,10 @@ pub const NonMontgomeryDomainFieldElement = [4]u64;
 inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
     @setRuntimeSafety(mode == .Debug);
 
-    var t: u64 = undefined;
-    const carry1 = @addWithOverflow(u64, arg2, arg3, &t);
-    const carry2 = @addWithOverflow(u64, t, arg1, out1);
-    out2.* = @boolToInt(carry1) | @boolToInt(carry2);
+    const ov1 = @addWithOverflow(arg2, arg3);
+    const ov2 = @addWithOverflow(ov1[0], arg1);
+    out1.* = ov2[0];
+    out2.* = ov1[1] | ov2[1];
 }
 
 /// The function subborrowxU64 is a subtraction with borrow.
@@ -97,10 +97,10 @@ inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) vo
 inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
     @setRuntimeSafety(mode == .Debug);
 
-    var t: u64 = undefined;
-    const carry1 = @subWithOverflow(u64, arg2, arg3, &t);
-    const carry2 = @subWithOverflow(u64, t, arg1, out1);
-    out2.* = @boolToInt(carry1) | @boolToInt(carry2);
+    const ov1 = @subWithOverflow(arg2, arg3);
+    const ov2 = @subWithOverflow(ov1[0], arg1);
+    out1.* = ov2[0];
+    out2.* = ov1[1] | ov2[1];
 }
 
 /// The function mulxU64 is a multiplication, returning the full double-width result.
lib/std/crypto/pcurves/p256/p256_scalar_64.zig
@@ -75,10 +75,10 @@ pub const NonMontgomeryDomainFieldElement = [4]u64;
 inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
     @setRuntimeSafety(mode == .Debug);
 
-    var t: u64 = undefined;
-    const carry1 = @addWithOverflow(u64, arg2, arg3, &t);
-    const carry2 = @addWithOverflow(u64, t, arg1, out1);
-    out2.* = @boolToInt(carry1) | @boolToInt(carry2);
+    const ov1 = @addWithOverflow(arg2, arg3);
+    const ov2 = @addWithOverflow(ov1[0], arg1);
+    out1.* = ov2[0];
+    out2.* = ov1[1] | ov2[1];
 }
 
 /// The function subborrowxU64 is a subtraction with borrow.
@@ -97,10 +97,10 @@ inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) vo
 inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
     @setRuntimeSafety(mode == .Debug);
 
-    var t: u64 = undefined;
-    const carry1 = @subWithOverflow(u64, arg2, arg3, &t);
-    const carry2 = @subWithOverflow(u64, t, arg1, out1);
-    out2.* = @boolToInt(carry1) | @boolToInt(carry2);
+    const ov1 = @subWithOverflow(arg2, arg3);
+    const ov2 = @subWithOverflow(ov1[0], arg1);
+    out1.* = ov2[0];
+    out2.* = ov1[1] | ov2[1];
 }
 
 /// The function mulxU64 is a multiplication, returning the full double-width result.
lib/std/crypto/pcurves/p384/p384_64.zig
@@ -44,10 +44,10 @@ pub const NonMontgomeryDomainFieldElement = [6]u64;
 inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
     @setRuntimeSafety(mode == .Debug);
 
-    var t: u64 = undefined;
-    const carry1 = @addWithOverflow(u64, arg2, arg3, &t);
-    const carry2 = @addWithOverflow(u64, t, arg1, out1);
-    out2.* = @boolToInt(carry1) | @boolToInt(carry2);
+    const ov1 = @addWithOverflow(arg2, arg3);
+    const ov2 = @addWithOverflow(ov1[0], arg1);
+    out1.* = ov2[0];
+    out2.* = ov1[1] | ov2[1];
 }
 
 /// The function subborrowxU64 is a subtraction with borrow.
@@ -66,10 +66,10 @@ inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) vo
 inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
     @setRuntimeSafety(mode == .Debug);
 
-    var t: u64 = undefined;
-    const carry1 = @subWithOverflow(u64, arg2, arg3, &t);
-    const carry2 = @subWithOverflow(u64, t, arg1, out1);
-    out2.* = @boolToInt(carry1) | @boolToInt(carry2);
+    const ov1 = @subWithOverflow(arg2, arg3);
+    const ov2 = @subWithOverflow(ov1[0], arg1);
+    out1.* = ov2[0];
+    out2.* = ov1[1] | ov2[1];
 }
 
 /// The function mulxU64 is a multiplication, returning the full double-width result.
lib/std/crypto/pcurves/p384/p384_scalar_64.zig
@@ -44,10 +44,10 @@ pub const NonMontgomeryDomainFieldElement = [6]u64;
 inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
     @setRuntimeSafety(mode == .Debug);
 
-    var t: u64 = undefined;
-    const carry1 = @addWithOverflow(u64, arg2, arg3, &t);
-    const carry2 = @addWithOverflow(u64, t, arg1, out1);
-    out2.* = @boolToInt(carry1) | @boolToInt(carry2);
+    const ov1 = @addWithOverflow(arg2, arg3);
+    const ov2 = @addWithOverflow(ov1[0], arg1);
+    out1.* = ov2[0];
+    out2.* = ov1[1] | ov2[1];
 }
 
 /// The function subborrowxU64 is a subtraction with borrow.
@@ -66,10 +66,10 @@ inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) vo
 inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
     @setRuntimeSafety(mode == .Debug);
 
-    var t: u64 = undefined;
-    const carry1 = @subWithOverflow(u64, arg2, arg3, &t);
-    const carry2 = @subWithOverflow(u64, t, arg1, out1);
-    out2.* = @boolToInt(carry1) | @boolToInt(carry2);
+    const ov1 = @subWithOverflow(arg2, arg3);
+    const ov2 = @subWithOverflow(ov1[0], arg1);
+    out1.* = ov2[0];
+    out2.* = ov1[1] | ov2[1];
 }
 
 /// The function mulxU64 is a multiplication, returning the full double-width result.
lib/std/crypto/pcurves/secp256k1/secp256k1_64.zig
@@ -44,10 +44,10 @@ pub const NonMontgomeryDomainFieldElement = [4]u64;
 inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
     @setRuntimeSafety(mode == .Debug);
 
-    var t: u64 = undefined;
-    const carry1 = @addWithOverflow(u64, arg2, arg3, &t);
-    const carry2 = @addWithOverflow(u64, t, arg1, out1);
-    out2.* = @boolToInt(carry1) | @boolToInt(carry2);
+    const ov1 = @addWithOverflow(arg2, arg3);
+    const ov2 = @addWithOverflow(ov1[0], arg1);
+    out1.* = ov2[0];
+    out2.* = ov1[1] | ov2[1];
 }
 
 /// The function subborrowxU64 is a subtraction with borrow.
@@ -66,10 +66,10 @@ inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) vo
 inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
     @setRuntimeSafety(mode == .Debug);
 
-    var t: u64 = undefined;
-    const carry1 = @subWithOverflow(u64, arg2, arg3, &t);
-    const carry2 = @subWithOverflow(u64, t, arg1, out1);
-    out2.* = @boolToInt(carry1) | @boolToInt(carry2);
+    const ov1 = @subWithOverflow(arg2, arg3);
+    const ov2 = @subWithOverflow(ov1[0], arg1);
+    out1.* = ov2[0];
+    out2.* = ov1[1] | ov2[1];
 }
 
 /// The function mulxU64 is a multiplication, returning the full double-width result.
lib/std/crypto/pcurves/secp256k1/secp256k1_scalar_64.zig
@@ -44,10 +44,10 @@ pub const NonMontgomeryDomainFieldElement = [4]u64;
 inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
     @setRuntimeSafety(mode == .Debug);
 
-    var t: u64 = undefined;
-    const carry1 = @addWithOverflow(u64, arg2, arg3, &t);
-    const carry2 = @addWithOverflow(u64, t, arg1, out1);
-    out2.* = @boolToInt(carry1) | @boolToInt(carry2);
+    const ov1 = @addWithOverflow(arg2, arg3);
+    const ov2 = @addWithOverflow(ov1[0], arg1);
+    out1.* = ov2[0];
+    out2.* = ov1[1] | ov2[1];
 }
 
 /// The function subborrowxU64 is a subtraction with borrow.
@@ -66,10 +66,10 @@ inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) vo
 inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
     @setRuntimeSafety(mode == .Debug);
 
-    var t: u64 = undefined;
-    const carry1 = @subWithOverflow(u64, arg2, arg3, &t);
-    const carry2 = @subWithOverflow(u64, t, arg1, out1);
-    out2.* = @boolToInt(carry1) | @boolToInt(carry2);
+    const ov1 = @subWithOverflow(arg2, arg3);
+    const ov2 = @subWithOverflow(ov1[0], arg1);
+    out1.* = ov2[0];
+    out2.* = ov1[1] | ov2[1];
 }
 
 /// The function mulxU64 is a multiplication, returning the full double-width result.
lib/std/crypto/salsa20.zig
@@ -263,7 +263,9 @@ fn SalsaNonVecImpl(comptime rounds: comptime_int) type {
                 while (j < 64) : (j += 1) {
                     xout[j] ^= buf[j];
                 }
-                ctx[9] += @boolToInt(@addWithOverflow(u32, ctx[8], 1, &ctx[8]));
+                const ov = @addWithOverflow(ctx[8], 1);
+                ctx[8] = ov[0];
+                ctx[9] += ov[1];
             }
             if (i < in.len) {
                 salsaCore(x[0..], ctx, true);
lib/std/crypto/utils.zig
@@ -87,15 +87,19 @@ pub fn timingSafeAdd(comptime T: type, a: []const T, b: []const T, result: []T,
     if (endian == .Little) {
         var i: usize = 0;
         while (i < len) : (i += 1) {
-            const tmp = @boolToInt(@addWithOverflow(u8, a[i], b[i], &result[i]));
-            carry = tmp | @boolToInt(@addWithOverflow(u8, result[i], carry, &result[i]));
+            const ov1 = @addWithOverflow(a[i], b[i]);
+            const ov2 = @addWithOverflow(ov1[0], carry);
+            result[i] = ov2[0];
+            carry = ov1[1] | ov2[1];
         }
     } else {
         var i: usize = len;
         while (i != 0) {
             i -= 1;
-            const tmp = @boolToInt(@addWithOverflow(u8, a[i], b[i], &result[i]));
-            carry = tmp | @boolToInt(@addWithOverflow(u8, result[i], carry, &result[i]));
+            const ov1 = @addWithOverflow(a[i], b[i]);
+            const ov2 = @addWithOverflow(ov1[0], carry);
+            result[i] = ov2[0];
+            carry = ov1[1] | ov2[1];
         }
     }
     return @bitCast(bool, carry);
@@ -110,15 +114,19 @@ pub fn timingSafeSub(comptime T: type, a: []const T, b: []const T, result: []T,
     if (endian == .Little) {
         var i: usize = 0;
         while (i < len) : (i += 1) {
-            const tmp = @boolToInt(@subWithOverflow(u8, a[i], b[i], &result[i]));
-            borrow = tmp | @boolToInt(@subWithOverflow(u8, result[i], borrow, &result[i]));
+            const ov1 = @subWithOverflow(a[i], b[i]);
+            const ov2 = @subWithOverflow(ov1[0], borrow);
+            result[i] = ov2[0];
+            borrow = ov1[1] | ov2[1];
         }
     } else {
         var i: usize = len;
         while (i != 0) {
             i -= 1;
-            const tmp = @boolToInt(@subWithOverflow(u8, a[i], b[i], &result[i]));
-            borrow = tmp | @boolToInt(@subWithOverflow(u8, result[i], borrow, &result[i]));
+            const ov1 = @subWithOverflow(a[i], b[i]);
+            const ov2 = @subWithOverflow(ov1[0], borrow);
+            result[i] = ov2[0];
+            borrow = ov1[1] | ov2[1];
         }
     }
     return @bitCast(bool, borrow);
lib/std/math/big/int.zig
@@ -74,42 +74,40 @@ pub fn calcTwosCompLimbCount(bit_count: usize) usize {
 /// a + b * c + *carry, sets carry to the overflow bits
 pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb {
     @setRuntimeSafety(debug_safety);
-    var r1: Limb = undefined;
 
-    // r1 = a + *carry
-    const c1: Limb = @boolToInt(@addWithOverflow(Limb, a, carry.*, &r1));
+    // ov1[0] = a + *carry
+    const ov1 = @addWithOverflow(a, carry.*);
 
     // r2 = b * c
     const bc = @as(DoubleLimb, math.mulWide(Limb, b, c));
     const r2 = @truncate(Limb, bc);
     const c2 = @truncate(Limb, bc >> limb_bits);
 
-    // r1 = r1 + r2
-    const c3: Limb = @boolToInt(@addWithOverflow(Limb, r1, r2, &r1));
+    // ov2[0] = ov1[0] + r2
+    const ov2 = @addWithOverflow(ov1[0], r2);
 
     // This never overflows, c1, c3 are either 0 or 1 and if both are 1 then
     // c2 is at least <= maxInt(Limb) - 2.
-    carry.* = c1 + c2 + c3;
+    carry.* = ov1[1] + c2 + ov2[1];
 
-    return r1;
+    return ov2[0];
 }
 
 /// a - b * c - *carry, sets carry to the overflow bits
 fn subMulLimbWithBorrow(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb {
-    // r1 = a - *carry
-    var r1: Limb = undefined;
-    const c1: Limb = @boolToInt(@subWithOverflow(Limb, a, carry.*, &r1));
+    // ov1[0] = a - *carry
+    const ov1 = @subWithOverflow(a, carry.*);
 
     // r2 = b * c
     const bc = @as(DoubleLimb, std.math.mulWide(Limb, b, c));
     const r2 = @truncate(Limb, bc);
     const c2 = @truncate(Limb, bc >> limb_bits);
 
-    // r1 = r1 - r2
-    const c3: Limb = @boolToInt(@subWithOverflow(Limb, r1, r2, &r1));
-    carry.* = c1 + c2 + c3;
+    // ov2[0] = ov1[0] - r2
+    const ov2 = @subWithOverflow(ov1[0], r2);
+    carry.* = ov1[1] + c2 + ov2[1];
 
-    return r1;
+    return ov2[0];
 }
 
 /// Used to indicate either limit of a 2s-complement integer.
@@ -673,7 +671,9 @@ pub const Mutable = struct {
         assert(rma.limbs.ptr != b.limbs.ptr); // illegal aliasing
 
         if (a.limbs.len == 1 and b.limbs.len == 1) {
-            if (!@mulWithOverflow(Limb, a.limbs[0], b.limbs[0], &rma.limbs[0])) {
+            const ov = @mulWithOverflow(a.limbs[0], b.limbs[0]);
+            rma.limbs[0] = ov[0];
+            if (ov[1] == 0) {
                 rma.len = 1;
                 rma.positive = (a.positive == b.positive);
                 return;
@@ -1836,7 +1836,11 @@ pub const Mutable = struct {
             bit_index += @bitSizeOf(Limb);
 
             // 2's complement (bitwise not, then add carry bit)
-            if (!positive) carry = @boolToInt(@addWithOverflow(Limb, ~limb, carry, &limb));
+            if (!positive) {
+                const ov = @addWithOverflow(~limb, carry);
+                limb = ov[0];
+                carry = ov[1];
+            }
             x.limbs[limb_index] = limb;
         }
 
@@ -1853,7 +1857,11 @@ pub const Mutable = struct {
             };
 
             // 2's complement (bitwise not, then add carry bit)
-            if (!positive) assert(!@addWithOverflow(Limb, ~limb, carry, &limb));
+            if (!positive) {
+                const ov = @addWithOverflow(~limb, carry);
+                assert(ov[1] == 0);
+                limb = ov[0];
+            }
             x.limbs[limb_index] = limb;
 
             limb_index += 1;
@@ -2000,7 +2008,9 @@ pub const Const = struct {
 
             // All but the most significant limb.
             for (self.limbs[0 .. self.limbs.len - 1]) |limb| {
-                carry = @boolToInt(@addWithOverflow(Limb, ~limb, carry, &add_res));
+                const ov = @addWithOverflow(~limb, carry);
+                add_res = ov[0];
+                carry = ov[1];
                 sum += @popCount(add_res);
                 remaining_bits -= limb_bits; // Asserted not to undeflow by fitsInTwosComp
             }
@@ -2294,7 +2304,11 @@ pub const Const = struct {
             var limb: Limb = if (limb_index < x.limbs.len) x.limbs[limb_index] else 0;
 
             // 2's complement (bitwise not, then add carry bit)
-            if (!x.positive) carry = @boolToInt(@addWithOverflow(Limb, ~limb, carry, &limb));
+            if (!x.positive) {
+                const ov = @addWithOverflow(~limb, carry);
+                limb = ov[0];
+                carry = ov[1];
+            }
 
             // Write one Limb of bits
             mem.writePackedInt(Limb, bytes, bit_index + bit_offset, limb, endian);
@@ -2306,7 +2320,7 @@ pub const Const = struct {
             var limb: Limb = if (limb_index < x.limbs.len) x.limbs[limb_index] else 0;
 
             // 2's complement (bitwise not, then add carry bit)
-            if (!x.positive) _ = @addWithOverflow(Limb, ~limb, carry, &limb);
+            if (!x.positive) limb = ~limb +% carry;
 
             // Write all remaining bits
             mem.writeVarPackedInt(bytes, bit_index + bit_offset, bit_count - bit_index, limb, endian);
@@ -3360,14 +3374,17 @@ fn llaccum(comptime op: AccOp, r: []Limb, a: []const Limb) void {
     var carry: Limb = 0;
 
     while (i < a.len) : (i += 1) {
-        var c: Limb = 0;
-        c += @boolToInt(@addWithOverflow(Limb, r[i], a[i], &r[i]));
-        c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i]));
-        carry = c;
+        const ov1 = @addWithOverflow(r[i], a[i]);
+        r[i] = ov1[0];
+        const ov2 = @addWithOverflow(r[i], carry);
+        r[i] = ov2[0];
+        carry = @as(Limb, ov1[1]) + ov2[1];
     }
 
     while ((carry != 0) and i < r.len) : (i += 1) {
-        carry = @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i]));
+        const ov = @addWithOverflow(r[i], carry);
+        r[i] = ov[0];
+        carry = ov[1];
     }
 }
 
@@ -3435,7 +3452,9 @@ fn llmulLimb(comptime op: AccOp, acc: []Limb, y: []const Limb, xi: Limb) bool {
 
             j = 0;
             while ((carry != 0) and (j < a_hi.len)) : (j += 1) {
-                carry = @boolToInt(@addWithOverflow(Limb, a_hi[j], carry, &a_hi[j]));
+                const ov = @addWithOverflow(a_hi[j], carry);
+                a_hi[j] = ov[0];
+                carry = ov[1];
             }
 
             return carry != 0;
@@ -3449,7 +3468,9 @@ fn llmulLimb(comptime op: AccOp, acc: []Limb, y: []const Limb, xi: Limb) bool {
 
             j = 0;
             while ((borrow != 0) and (j < a_hi.len)) : (j += 1) {
-                borrow = @boolToInt(@subWithOverflow(Limb, a_hi[j], borrow, &a_hi[j]));
+                const ov = @subWithOverflow(a_hi[j], borrow);
+                a_hi[j] = ov[0];
+                borrow = ov[1];
             }
 
             return borrow != 0;
@@ -3482,14 +3503,17 @@ fn llsubcarry(r: []Limb, a: []const Limb, b: []const Limb) Limb {
     var borrow: Limb = 0;
 
     while (i < b.len) : (i += 1) {
-        var c: Limb = 0;
-        c += @boolToInt(@subWithOverflow(Limb, a[i], b[i], &r[i]));
-        c += @boolToInt(@subWithOverflow(Limb, r[i], borrow, &r[i]));
-        borrow = c;
+        const ov1 = @subWithOverflow(a[i], b[i]);
+        r[i] = ov1[0];
+        const ov2 = @subWithOverflow(r[i], borrow);
+        r[i] = ov2[0];
+        borrow = @as(Limb, ov1[1]) + ov2[1];
     }
 
     while (i < a.len) : (i += 1) {
-        borrow = @boolToInt(@subWithOverflow(Limb, a[i], borrow, &r[i]));
+        const ov = @subWithOverflow(a[i], borrow);
+        r[i] = ov[0];
+        borrow = ov[1];
     }
 
     return borrow;
@@ -3512,14 +3536,17 @@ fn lladdcarry(r: []Limb, a: []const Limb, b: []const Limb) Limb {
     var carry: Limb = 0;
 
     while (i < b.len) : (i += 1) {
-        var c: Limb = 0;
-        c += @boolToInt(@addWithOverflow(Limb, a[i], b[i], &r[i]));
-        c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i]));
-        carry = c;
+        const ov1 = @addWithOverflow(a[i], b[i]);
+        r[i] = ov1[0];
+        const ov2 = @addWithOverflow(r[i], carry);
+        r[i] = ov2[0];
+        carry = @as(Limb, ov1[1]) + ov2[1];
     }
 
     while (i < a.len) : (i += 1) {
-        carry = @boolToInt(@addWithOverflow(Limb, a[i], carry, &r[i]));
+        const ov = @addWithOverflow(a[i], carry);
+        r[i] = ov[0];
+        carry = ov[1];
     }
 
     return carry;
@@ -3685,11 +3712,11 @@ fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_p
         var r_carry: u1 = 1;
 
         while (i < b.len) : (i += 1) {
-            var a_limb: Limb = undefined;
-            a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb));
-
-            r[i] = a_limb & ~b[i];
-            r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
+            const ov1 = @subWithOverflow(a[i], a_borrow);
+            a_borrow = ov1[1];
+            const ov2 = @addWithOverflow(ov1[0] & ~b[i], r_carry);
+            r[i] = ov2[0];
+            r_carry = ov2[1];
         }
 
         // In order for r_carry to be nonzero at this point, ~b[i] would need to be
@@ -3702,7 +3729,9 @@ fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_p
         // Note, if a_borrow is zero we do not need to compute anything for
         // the higher limbs so we can early return here.
         while (i < a.len and a_borrow == 1) : (i += 1) {
-            a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &r[i]));
+            const ov = @subWithOverflow(a[i], a_borrow);
+            r[i] = ov[0];
+            a_borrow = ov[1];
         }
 
         assert(a_borrow == 0); // a was 0.
@@ -3721,11 +3750,11 @@ fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_p
         var r_carry: u1 = 1;
 
         while (i < b.len) : (i += 1) {
-            var b_limb: Limb = undefined;
-            b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &b_limb));
-
-            r[i] = ~a[i] & b_limb;
-            r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
+            const ov1 = @subWithOverflow(b[i], b_borrow);
+            b_borrow = ov1[1];
+            const ov2 = @addWithOverflow(~a[i] & ov1[0], r_carry);
+            r[i] = ov2[0];
+            r_carry = ov2[1];
         }
 
         // b is at least 1, so this should never underflow.
@@ -3752,14 +3781,13 @@ fn llsignedor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_p
         var r_carry: u1 = 1;
 
         while (i < b.len) : (i += 1) {
-            var a_limb: Limb = undefined;
-            a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb));
-
-            var b_limb: Limb = undefined;
-            b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &b_limb));
-
-            r[i] = a_limb & b_limb;
-            r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
+            const ov1 = @subWithOverflow(a[i], a_borrow);
+            a_borrow = ov1[1];
+            const ov2 = @subWithOverflow(b[i], b_borrow);
+            b_borrow = ov2[1];
+            const ov3 = @addWithOverflow(ov1[0] & ov2[0], r_carry);
+            r[i] = ov3[0];
+            r_carry = ov3[1];
         }
 
         // b is at least 1, so this should never underflow.
@@ -3811,9 +3839,9 @@ fn llsignedand(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_
         var a_borrow: u1 = 1;
 
         while (i < b.len) : (i += 1) {
-            var a_limb: Limb = undefined;
-            a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb));
-            r[i] = ~a_limb & b[i];
+            const ov = @subWithOverflow(a[i], a_borrow);
+            a_borrow = ov[1];
+            r[i] = ~ov[0] & b[i];
         }
 
         // With b = 0 we have ~(a - 1) & 0 = 0, so the upper bytes are zero.
@@ -3830,9 +3858,9 @@ fn llsignedand(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_
         var b_borrow: u1 = 1;
 
         while (i < b.len) : (i += 1) {
-            var a_limb: Limb = undefined;
-            b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &a_limb));
-            r[i] = a[i] & ~a_limb;
+            const ov = @subWithOverflow(b[i], b_borrow);
+            b_borrow = ov[1];
+            r[i] = a[i] & ~ov[0];
         }
 
         assert(b_borrow == 0); // b was 0
@@ -3855,14 +3883,13 @@ fn llsignedand(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_
         var r_carry: u1 = 1;
 
         while (i < b.len) : (i += 1) {
-            var a_limb: Limb = undefined;
-            a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb));
-
-            var b_limb: Limb = undefined;
-            b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &b_limb));
-
-            r[i] = a_limb | b_limb;
-            r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
+            const ov1 = @subWithOverflow(a[i], a_borrow);
+            a_borrow = ov1[1];
+            const ov2 = @subWithOverflow(b[i], b_borrow);
+            b_borrow = ov2[1];
+            const ov3 = @addWithOverflow(ov1[0] | ov2[0], r_carry);
+            r[i] = ov3[0];
+            r_carry = ov3[1];
         }
 
         // b is at least 1, so this should never underflow.
@@ -3870,8 +3897,11 @@ fn llsignedand(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_
 
         // With b = 0 and b_borrow = 0 we get (-a - 1) | (-0 - 0) = (-a - 1) | 0 = -a - 1.
         while (i < a.len) : (i += 1) {
-            a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &r[i]));
-            r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
+            const ov1 = @subWithOverflow(a[i], a_borrow);
+            a_borrow = ov1[1];
+            const ov2 = @addWithOverflow(ov1[0], r_carry);
+            r[i] = ov2[0];
+            r_carry = ov2[1];
         }
 
         assert(a_borrow == 0); // a was 0.
@@ -3917,19 +3947,21 @@ fn llsignedxor(r: []Limb, a: []const Limb, a_positive: bool, b: []const Limb, b_
     var r_carry = @boolToInt(a_positive != b_positive);
 
     while (i < b.len) : (i += 1) {
-        var a_limb: Limb = undefined;
-        a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb));
-
-        var b_limb: Limb = undefined;
-        b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &b_limb));
-
-        r[i] = a_limb ^ b_limb;
-        r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
+        const ov1 = @subWithOverflow(a[i], a_borrow);
+        a_borrow = ov1[1];
+        const ov2 = @subWithOverflow(b[i], b_borrow);
+        b_borrow = ov2[1];
+        const ov3 = @addWithOverflow(ov1[0] ^ ov2[0], r_carry);
+        r[i] = ov3[0];
+        r_carry = ov3[1];
     }
 
     while (i < a.len) : (i += 1) {
-        a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &r[i]));
-        r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
+        const ov1 = @subWithOverflow(a[i], a_borrow);
+        a_borrow = ov1[1];
+        const ov2 = @addWithOverflow(ov1[0], r_carry);
+        r[i] = ov2[0];
+        r_carry = ov2[1];
     }
 
     // If both inputs don't share the same sign, an extra limb is required.
@@ -4021,7 +4053,9 @@ fn llpow(r: []Limb, a: []const Limb, b: u32, tmp_limbs: []Limb) void {
         llsquareBasecase(tmp2, tmp1[0..llnormalize(tmp1)]);
         mem.swap([]Limb, &tmp1, &tmp2);
         // Multiply by a
-        if (@shlWithOverflow(u32, exp, 1, &exp)) {
+        const ov = @shlWithOverflow(exp, 1);
+        exp = ov[0];
+        if (ov[1] != 0) {
             mem.set(Limb, tmp2, 0);
             llmulacc(.add, null, tmp2, tmp1[0..llnormalize(tmp1)], a);
             mem.swap([]Limb, &tmp1, &tmp2);
lib/std/math/powi.zig
@@ -70,22 +70,22 @@ pub fn powi(comptime T: type, x: T, y: T) (error{
 
     while (exp > 1) {
         if (exp & 1 == 1) {
-            if (@mulWithOverflow(T, acc, base, &acc)) {
-                return error.Overflow;
-            }
+            const ov = @mulWithOverflow(acc, base);
+            if (ov[1] != 0) return error.Overflow;
+            acc = ov[0];
         }
 
         exp >>= 1;
 
-        if (@mulWithOverflow(T, base, base, &base)) {
-            return error.Overflow;
-        }
+        const ov = @mulWithOverflow(base, base);
+        if (ov[1] != 0) return error.Overflow;
+        base = ov[0];
     }
 
     if (exp == 1) {
-        if (@mulWithOverflow(T, acc, base, &acc)) {
-            return error.Overflow;
-        }
+        const ov = @mulWithOverflow(acc, base);
+        if (ov[1] != 0) return error.Overflow;
+        acc = ov[0];
     }
 
     return acc;
lib/std/os/linux.zig
@@ -1244,7 +1244,7 @@ pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize
             var size: i32 = 0;
             const msg_iovlen = @intCast(usize, msg.msg_hdr.msg_iovlen); // kernel side this is treated as unsigned
             for (msg.msg_hdr.msg_iov[0..msg_iovlen]) |iov| {
-                if (iov.iov_len > std.math.maxInt(i32) or @addWithOverflow(i32, size, @intCast(i32, iov.iov_len), &size)) {
+                if (iov.iov_len > std.math.maxInt(i32) or @addWithOverflow(size, @intCast(i32, iov.iov_len))[1] != 0) {
                     // batch-send all messages up to the current message
                     if (next_unsent < i) {
                         const batch_size = i - next_unsent;
lib/std/zig/c_builtins.zig
@@ -246,7 +246,9 @@ pub inline fn __builtin_constant_p(expr: anytype) c_int {
     return @boolToInt(false);
 }
 pub fn __builtin_mul_overflow(a: anytype, b: anytype, result: *@TypeOf(a, b)) c_int {
-    return @boolToInt(@mulWithOverflow(@TypeOf(a, b), a, b, result));
+    const res = @mulWithOverflow(a, b);
+    result.* = res[0];
+    return res[1];
 }
 
 // __builtin_alloca_with_align is not currently implemented.
lib/std/zig/number_literal.zig
@@ -151,12 +151,14 @@ pub fn parseNumberLiteral(bytes: []const u8) Result {
         special = 0;
 
         if (float) continue;
-        if (x != 0) if (@mulWithOverflow(u64, x, base, &x)) {
-            overflow = true;
-        };
-        if (@addWithOverflow(u64, x, digit, &x)) {
-            overflow = true;
+        if (x != 0) {
+            const res = @mulWithOverflow(x, base);
+            if (res[1] != 0) overflow = true;
+            x = res[0];
         }
+        const res = @addWithOverflow(x, digit);
+        if (res[1] != 0) overflow = true;
+        x = res[0];
     }
     if (underscore) return .{ .failure = .{ .trailing_underscore = bytes.len - 1 } };
     if (special != 0) return .{ .failure = .{ .trailing_special = bytes.len - 1 } };
lib/std/heap.zig
@@ -789,7 +789,7 @@ pub fn testAllocatorLargeAlignment(base_allocator: mem.Allocator) !void {
     const large_align: usize = mem.page_size / 2;
 
     var align_mask: usize = undefined;
-    _ = @shlWithOverflow(usize, ~@as(usize, 0), @as(Allocator.Log2Align, @ctz(large_align)), &align_mask);
+    align_mask = @shlWithOverflow(~@as(usize, 0), @as(Allocator.Log2Align, @ctz(large_align)))[0];
 
     var slice = try allocator.alignedAlloc(u8, large_align, 500);
     try testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
lib/std/leb128.zig
@@ -15,11 +15,11 @@ pub fn readULEB128(comptime T: type, reader: anytype) !T {
 
     while (group < max_group) : (group += 1) {
         const byte = try reader.readByte();
-        var temp = @as(U, byte & 0x7f);
 
-        if (@shlWithOverflow(U, temp, group * 7, &temp)) return error.Overflow;
+        const ov = @shlWithOverflow(@as(U, byte & 0x7f), group * 7);
+        if (ov[1] != 0) return error.Overflow;
 
-        value |= temp;
+        value |= ov[0];
         if (byte & 0x80 == 0) break;
     } else {
         return error.Overflow;
@@ -65,13 +65,13 @@ pub fn readILEB128(comptime T: type, reader: anytype) !T {
 
     while (group < max_group) : (group += 1) {
         const byte = try reader.readByte();
-        var temp = @as(U, byte & 0x7f);
 
         const shift = group * 7;
-        if (@shlWithOverflow(U, temp, shift, &temp)) {
+        const ov = @shlWithOverflow(@as(U, byte & 0x7f), shift);
+        if (ov[1] != 0) {
             // Overflow is ok so long as the sign bit is set and this is the last byte
             if (byte & 0x80 != 0) return error.Overflow;
-            if (@bitCast(S, temp) >= 0) return error.Overflow;
+            if (@bitCast(S, ov[0]) >= 0) return error.Overflow;
 
             // and all the overflowed bits are 1
             const remaining_shift = @intCast(u3, @typeInfo(U).Int.bits - @as(u16, shift));
@@ -80,14 +80,14 @@ pub fn readILEB128(comptime T: type, reader: anytype) !T {
         } else {
             // If we don't overflow and this is the last byte and the number being decoded
             // is negative, check that the remaining bits are 1
-            if ((byte & 0x80 == 0) and (@bitCast(S, temp) < 0)) {
+            if ((byte & 0x80 == 0) and (@bitCast(S, ov[0]) < 0)) {
                 const remaining_shift = @intCast(u3, @typeInfo(U).Int.bits - @as(u16, shift));
                 const remaining_bits = @bitCast(i8, byte | 0x80) >> remaining_shift;
                 if (remaining_bits != -1) return error.Overflow;
             }
         }
 
-        value |= temp;
+        value |= ov[0];
         if (byte & 0x80 == 0) {
             const needs_sign_ext = group + 1 < max_group;
             if (byte & 0x40 != 0 and needs_sign_ext) {
lib/std/math.zig
@@ -468,21 +468,26 @@ test "clamp" {
 
 /// Returns the product of a and b. Returns an error on overflow.
 pub fn mul(comptime T: type, a: T, b: T) (error{Overflow}!T) {
-    var answer: T = undefined;
-    return if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer;
+    if (T == comptime_int) return a * b;
+    const ov = @mulWithOverflow(a, b);
+    if (ov[1] != 0) return error.Overflow;
+    return ov[0];
 }
 
 /// Returns the sum of a and b. Returns an error on overflow.
 pub fn add(comptime T: type, a: T, b: T) (error{Overflow}!T) {
     if (T == comptime_int) return a + b;
-    var answer: T = undefined;
-    return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer;
+    const ov = @addWithOverflow(a, b);
+    if (ov[1] != 0) return error.Overflow;
+    return ov[0];
 }
 
 /// Returns a - b, or an error on overflow.
 pub fn sub(comptime T: type, a: T, b: T) (error{Overflow}!T) {
-    var answer: T = undefined;
-    return if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer;
+    if (T == comptime_int) return a - b;
+    const ov = @subWithOverflow(a, b);
+    if (ov[1] != 0) return error.Overflow;
+    return ov[0];
 }
 
 pub fn negate(x: anytype) !@TypeOf(x) {
@@ -492,8 +497,10 @@ pub fn negate(x: anytype) !@TypeOf(x) {
 /// Shifts a left by shift_amt. Returns an error on overflow. shift_amt
 /// is unsigned.
 pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T {
-    var answer: T = undefined;
-    return if (@shlWithOverflow(T, a, shift_amt, &answer)) error.Overflow else answer;
+    if (T == comptime_int) return a << shift_amt;
+    const ov = @shlWithOverflow(a, shift_amt);
+    if (ov[1] != 0) return error.Overflow;
+    return ov[0];
 }
 
 /// Shifts left. Overflowed bits are truncated.
lib/std/mem.zig
@@ -3304,13 +3304,13 @@ pub fn alignPointerOffset(ptr: anytype, align_to: usize) ?usize {
 
     // Calculate the aligned base address with an eye out for overflow.
     const addr = @ptrToInt(ptr);
-    var new_addr: usize = undefined;
-    if (@addWithOverflow(usize, addr, align_to - 1, &new_addr)) return null;
-    new_addr &= ~@as(usize, align_to - 1);
+    var ov = @addWithOverflow(addr, align_to - 1);
+    if (ov[1] != 0) return null;
+    ov[0] &= ~@as(usize, align_to - 1);
 
     // The delta is expressed in terms of bytes, turn it into a number of child
     // type elements.
-    const delta = new_addr - addr;
+    const delta = ov[0] - addr;
     const pointee_size = @sizeOf(info.Pointer.child);
     if (delta % pointee_size != 0) return null;
     return delta / pointee_size;
lib/std/net.zig
@@ -321,11 +321,15 @@ pub const Ip6Address = extern struct {
             if (scope_id) {
                 if (c >= '0' and c <= '9') {
                     const digit = c - '0';
-                    if (@mulWithOverflow(u32, result.sa.scope_id, 10, &result.sa.scope_id)) {
-                        return error.Overflow;
+                    {
+                        const ov = @mulWithOverflow(result.sa.scope_id, 10);
+                        if (ov[1] != 0) return error.Overflow;
+                        result.sa.scope_id = ov[0];
                     }
-                    if (@addWithOverflow(u32, result.sa.scope_id, digit, &result.sa.scope_id)) {
-                        return error.Overflow;
+                    {
+                        const ov = @addWithOverflow(result.sa.scope_id, digit);
+                        if (ov[1] != 0) return error.Overflow;
+                        result.sa.scope_id = ov[0];
                     }
                 } else {
                     return error.InvalidCharacter;
@@ -377,11 +381,15 @@ pub const Ip6Address = extern struct {
                 return result;
             } else {
                 const digit = try std.fmt.charToDigit(c, 16);
-                if (@mulWithOverflow(u16, x, 16, &x)) {
-                    return error.Overflow;
+                {
+                    const ov = @mulWithOverflow(x, 16);
+                    if (ov[1] != 0) return error.Overflow;
+                    x = ov[0];
                 }
-                if (@addWithOverflow(u16, x, digit, &x)) {
-                    return error.Overflow;
+                {
+                    const ov = @addWithOverflow(x, digit);
+                    if (ov[1] != 0) return error.Overflow;
+                    x = ov[0];
                 }
                 saw_any_digits = true;
             }
@@ -492,11 +500,15 @@ pub const Ip6Address = extern struct {
                 return result;
             } else {
                 const digit = try std.fmt.charToDigit(c, 16);
-                if (@mulWithOverflow(u16, x, 16, &x)) {
-                    return error.Overflow;
+                {
+                    const ov = @mulWithOverflow(x, 16);
+                    if (ov[1] != 0) return error.Overflow;
+                    x = ov[0];
                 }
-                if (@addWithOverflow(u16, x, digit, &x)) {
-                    return error.Overflow;
+                {
+                    const ov = @addWithOverflow(x, digit);
+                    if (ov[1] != 0) return error.Overflow;
+                    x = ov[0];
                 }
                 saw_any_digits = true;
             }
lib/std/process.zig
@@ -1023,8 +1023,16 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {
                             '0'...'9' => byte - '0',
                             else => return error.CorruptPasswordFile,
                         };
-                        if (@mulWithOverflow(u32, uid, 10, &uid)) return error.CorruptPasswordFile;
-                        if (@addWithOverflow(u32, uid, digit, &uid)) return error.CorruptPasswordFile;
+                        {
+                            const ov = @mulWithOverflow(uid, 10);
+                            if (ov[1] != 0) return error.CorruptPasswordFile;
+                            uid = ov[0];
+                        }
+                        {
+                            const ov = @addWithOverflow(uid, digit);
+                            if (ov[1] != 0) return error.CorruptPasswordFile;
+                            uid = ov[0];
+                        }
                     },
                 },
                 .ReadGroupId => switch (byte) {
@@ -1039,8 +1047,16 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {
                             '0'...'9' => byte - '0',
                             else => return error.CorruptPasswordFile,
                         };
-                        if (@mulWithOverflow(u32, gid, 10, &gid)) return error.CorruptPasswordFile;
-                        if (@addWithOverflow(u32, gid, digit, &gid)) return error.CorruptPasswordFile;
+                        {
+                            const ov = @mulWithOverflow(gid, 10);
+                            if (ov[1] != 0) return error.CorruptPasswordFile;
+                            gid = ov[0];
+                        }
+                        {
+                            const ov = @addWithOverflow(gid, digit);
+                            if (ov[1] != 0) return error.CorruptPasswordFile;
+                            gid = ov[0];
+                        }
                     },
                 },
             }
src/link/Coff.zig
@@ -1860,9 +1860,7 @@ fn writeHeader(self: *Coff) !void {
 }
 
 pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
-    // TODO https://github.com/ziglang/zig/issues/1284
-    return math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch
-        math.maxInt(@TypeOf(actual_size));
+    return actual_size +| (actual_size / ideal_factor);
 }
 
 fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 {
src/link/Dwarf.zig
@@ -2445,9 +2445,7 @@ fn makeString(self: *Dwarf, bytes: []const u8) !u32 {
 }
 
 fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
-    // TODO https://github.com/ziglang/zig/issues/1284
-    return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch
-        std.math.maxInt(@TypeOf(actual_size));
+    return actual_size +| (actual_size / ideal_factor);
 }
 
 pub fn flushModule(self: *Dwarf, module: *Module) !void {
src/link/Elf.zig
@@ -3032,9 +3032,7 @@ fn getLDMOption(target: std.Target) ?[]const u8 {
 }
 
 fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
-    // TODO https://github.com/ziglang/zig/issues/1284
-    return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch
-        std.math.maxInt(@TypeOf(actual_size));
+    return actual_size +| (actual_size / ideal_factor);
 }
 
 // Provide a blueprint of csu (c-runtime startup) objects for supported
src/link/MachO.zig
@@ -3772,9 +3772,7 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
 }
 
 pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
-    // TODO https://github.com/ziglang/zig/issues/1284
-    return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch
-        std.math.maxInt(@TypeOf(actual_size));
+    return actual_size +| (actual_size / ideal_factor);
 }
 
 fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
test/behavior/eval.zig
@@ -489,18 +489,11 @@ test "comptime bitwise operators" {
 
 test "comptime shlWithOverflow" {
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
 
-    const ct_shifted: u64 = comptime amt: {
-        var amt = @as(u64, 0);
-        _ = @shlWithOverflow(u64, ~@as(u64, 0), 16, &amt);
-        break :amt amt;
-    };
-
-    const rt_shifted: u64 = amt: {
-        var amt = @as(u64, 0);
-        _ = @shlWithOverflow(u64, ~@as(u64, 0), 16, &amt);
-        break :amt amt;
-    };
+    const ct_shifted = @shlWithOverflow(~@as(u64, 0), 16)[0];
+    var a = ~@as(u64, 0);
+    const rt_shifted = @shlWithOverflow(a, 16)[0];
 
     try expect(ct_shifted == rt_shifted);
 }
test/behavior/math.zig
@@ -534,6 +534,7 @@ fn testUnsignedNegationWrappingEval(x: u16) !void {
 
 test "negation wrapping" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
 
     try expectEqual(@as(u1, 1), negateWrap(u1, 1));
@@ -634,42 +635,53 @@ test "128-bit multiplication" {
 }
 
 test "@addWithOverflow" {
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
 
     {
-        var result: u8 = undefined;
-        try expect(@addWithOverflow(u8, 250, 100, &result));
-        try expect(result == 94);
-        try expect(!@addWithOverflow(u8, 100, 150, &result));
-        try expect(result == 250);
-
+        var a: u8 = 250;
+        const ov = @addWithOverflow(a, 100);
+        try expect(ov[0] == 94);
+        try expect(ov[1] == 1);
+    }
+    {
+        var a: u8 = 100;
+        const ov = @addWithOverflow(a, 150);
+        try expect(ov[0] == 250);
+        try expect(ov[1] == 0);
+    }
+    {
         var a: u8 = 200;
         var b: u8 = 99;
-        try expect(@addWithOverflow(u8, a, b, &result));
-        try expect(result == 43);
+        var ov = @addWithOverflow(a, b);
+        try expect(ov[0] == 43);
+        try expect(ov[1] == 1);
         b = 55;
-        try expect(!@addWithOverflow(u8, a, b, &result));
-        try expect(result == 255);
+        ov = @addWithOverflow(a, b);
+        try expect(ov[0] == 255);
+        try expect(ov[1] == 0);
     }
 
     {
         var a: usize = 6;
         var b: usize = 6;
-        var res: usize = undefined;
-        try expect(!@addWithOverflow(usize, a, b, &res));
-        try expect(res == 12);
+        const ov = @addWithOverflow(a, b);
+        try expect(ov[0] == 12);
+        try expect(ov[1] == 0);
     }
 
     {
         var a: isize = -6;
         var b: isize = -6;
-        var res: isize = undefined;
-        try expect(!@addWithOverflow(isize, a, b, &res));
-        try expect(res == -12);
+        const ov = @addWithOverflow(a, b);
+        try expect(ov[0] == -12);
+        try expect(ov[1] == 0);
     }
 }
 
 test "small int addition" {
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
 
     var x: u2 = 0;
@@ -684,180 +696,206 @@ test "small int addition" {
     x += 1;
     try expect(x == 3);
 
-    var result: @TypeOf(x) = 3;
-    try expect(@addWithOverflow(@TypeOf(x), x, 1, &result));
-
-    try expect(result == 0);
+    const ov = @addWithOverflow(x, 1);
+    try expect(ov[0] == 0);
+    try expect(ov[1] == 1);
 }
 
 test "basic @mulWithOverflow" {
-    var result: u8 = undefined;
-    try expect(@mulWithOverflow(u8, 86, 3, &result));
-    try expect(result == 2);
-    try expect(!@mulWithOverflow(u8, 85, 3, &result));
-    try expect(result == 255);
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+
+    {
+        var a: u8 = 86;
+        const ov = @mulWithOverflow(a, 3);
+        try expect(ov[0] == 2);
+        try expect(ov[1] == 1);
+    }
+    {
+        var a: u8 = 85;
+        const ov = @mulWithOverflow(a, 3);
+        try expect(ov[0] == 255);
+        try expect(ov[1] == 0);
+    }
 
     var a: u8 = 123;
     var b: u8 = 2;
-    try expect(!@mulWithOverflow(u8, a, b, &result));
-    try expect(result == 246);
+    var ov = @mulWithOverflow(a, b);
+    try expect(ov[0] == 246);
+    try expect(ov[1] == 0);
 
     b = 4;
-    try expect(@mulWithOverflow(u8, a, b, &result));
-    try expect(result == 236);
+    ov = @mulWithOverflow(a, b);
+    try expect(ov[0] == 236);
+    try expect(ov[1] == 1);
 }
 
-// TODO migrate to this for all backends once they handle more cases
 test "extensive @mulWithOverflow" {
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
 
     {
         var a: u5 = 3;
         var b: u5 = 10;
-        var res: u5 = undefined;
-        try expect(!@mulWithOverflow(u5, a, b, &res));
-        try expect(res == 30);
+        var ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 30);
+        try expect(ov[1] == 0);
 
         b = 11;
-        try expect(@mulWithOverflow(u5, a, b, &res));
-        try expect(res == 1);
+        ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 1);
+        try expect(ov[1] == 1);
     }
 
     {
         var a: i5 = 3;
         var b: i5 = -5;
-        var res: i5 = undefined;
-        try expect(!@mulWithOverflow(i5, a, b, &res));
-        try expect(res == -15);
+        var ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == -15);
+        try expect(ov[1] == 0);
 
         b = -6;
-        try expect(@mulWithOverflow(i5, a, b, &res));
-        try expect(res == 14);
+        ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 14);
+        try expect(ov[1] == 1);
     }
 
     {
         var a: u8 = 3;
         var b: u8 = 85;
-        var res: u8 = undefined;
 
-        try expect(!@mulWithOverflow(u8, a, b, &res));
-        try expect(res == 255);
+        var ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 255);
+        try expect(ov[1] == 0);
 
         b = 86;
-        try expect(@mulWithOverflow(u8, a, b, &res));
-        try expect(res == 2);
+        ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 2);
+        try expect(ov[1] == 1);
     }
 
     {
         var a: i8 = 3;
         var b: i8 = -42;
-        var res: i8 = undefined;
-        try expect(!@mulWithOverflow(i8, a, b, &res));
-        try expect(res == -126);
+        var ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == -126);
+        try expect(ov[1] == 0);
 
         b = -43;
-        try expect(@mulWithOverflow(i8, a, b, &res));
-        try expect(res == 127);
+        ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 127);
+        try expect(ov[1] == 1);
     }
 
     {
         var a: u14 = 3;
         var b: u14 = 0x1555;
-        var res: u14 = undefined;
-        try expect(!@mulWithOverflow(u14, a, b, &res));
-        try expect(res == 0x3fff);
+        var ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 0x3fff);
+        try expect(ov[1] == 0);
 
         b = 0x1556;
-        try expect(@mulWithOverflow(u14, a, b, &res));
-        try expect(res == 2);
+        ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 2);
+        try expect(ov[1] == 1);
     }
 
     {
         var a: i14 = 3;
         var b: i14 = -0xaaa;
-        var res: i14 = undefined;
-        try expect(!@mulWithOverflow(i14, a, b, &res));
-        try expect(res == -0x1ffe);
+        var ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == -0x1ffe);
+        try expect(ov[1] == 0);
 
         b = -0xaab;
-        try expect(@mulWithOverflow(i14, a, b, &res));
-        try expect(res == 0x1fff);
+        ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 0x1fff);
     }
 
     {
         var a: u16 = 3;
         var b: u16 = 0x5555;
-        var res: u16 = undefined;
-        try expect(!@mulWithOverflow(u16, a, b, &res));
-        try expect(res == 0xffff);
+        var ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 0xffff);
+        try expect(ov[1] == 0);
 
         b = 0x5556;
-        try expect(@mulWithOverflow(u16, a, b, &res));
-        try expect(res == 2);
+        ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 2);
+        try expect(ov[1] == 1);
     }
 
     {
         var a: i16 = 3;
         var b: i16 = -0x2aaa;
-        var res: i16 = undefined;
-        try expect(!@mulWithOverflow(i16, a, b, &res));
-        try expect(res == -0x7ffe);
+        var ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == -0x7ffe);
+        try expect(ov[1] == 0);
 
         b = -0x2aab;
-        try expect(@mulWithOverflow(i16, a, b, &res));
-        try expect(res == 0x7fff);
+        ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 0x7fff);
+        try expect(ov[1] == 1);
     }
 
     {
         var a: u30 = 3;
         var b: u30 = 0x15555555;
-        var res: u30 = undefined;
-        try expect(!@mulWithOverflow(u30, a, b, &res));
-        try expect(res == 0x3fffffff);
+        var ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 0x3fffffff);
+        try expect(ov[1] == 0);
 
         b = 0x15555556;
-        try expect(@mulWithOverflow(u30, a, b, &res));
-        try expect(res == 2);
+        ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 2);
+        try expect(ov[1] == 1);
     }
 
     {
         var a: i30 = 3;
         var b: i30 = -0xaaaaaaa;
-        var res: i30 = undefined;
-        try expect(!@mulWithOverflow(i30, a, b, &res));
-        try expect(res == -0x1ffffffe);
+        var ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == -0x1ffffffe);
+        try expect(ov[1] == 0);
 
         b = -0xaaaaaab;
-        try expect(@mulWithOverflow(i30, a, b, &res));
-        try expect(res == 0x1fffffff);
+        ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 0x1fffffff);
+        try expect(ov[1] == 1);
     }
 
     {
         var a: u32 = 3;
         var b: u32 = 0x55555555;
-        var res: u32 = undefined;
-        try expect(!@mulWithOverflow(u32, a, b, &res));
-        try expect(res == 0xffffffff);
+        var ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 0xffffffff);
+        try expect(ov[1] == 0);
 
         b = 0x55555556;
-        try expect(@mulWithOverflow(u32, a, b, &res));
-        try expect(res == 2);
+        ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 2);
+        try expect(ov[1] == 1);
     }
 
     {
         var a: i32 = 3;
         var b: i32 = -0x2aaaaaaa;
-        var res: i32 = undefined;
-        try expect(!@mulWithOverflow(i32, a, b, &res));
-        try expect(res == -0x7ffffffe);
+        var ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == -0x7ffffffe);
+        try expect(ov[1] == 0);
 
         b = -0x2aaaaaab;
-        try expect(@mulWithOverflow(i32, a, b, &res));
-        try expect(res == 0x7fffffff);
+        ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 0x7fffffff);
+        try expect(ov[1] == 1);
     }
 }
 
 test "@mulWithOverflow bitsize > 32" {
+    // aarch64 fails on a release build of the compiler.
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
@@ -865,140 +903,181 @@ test "@mulWithOverflow bitsize > 32" {
     {
         var a: u62 = 3;
         var b: u62 = 0x1555555555555555;
-        var res: u62 = undefined;
-        try expect(!@mulWithOverflow(u62, a, b, &res));
-        try expect(res == 0x3fffffffffffffff);
+        var ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 0x3fffffffffffffff);
+        try expect(ov[1] == 0);
 
         b = 0x1555555555555556;
-        try expect(@mulWithOverflow(u62, a, b, &res));
-        try expect(res == 2);
+        ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 2);
+        try expect(ov[1] == 1);
     }
 
     {
         var a: i62 = 3;
         var b: i62 = -0xaaaaaaaaaaaaaaa;
-        var res: i62 = undefined;
-        try expect(!@mulWithOverflow(i62, a, b, &res));
-        try expect(res == -0x1ffffffffffffffe);
+        var ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == -0x1ffffffffffffffe);
+        try expect(ov[1] == 0);
 
         b = -0xaaaaaaaaaaaaaab;
-        try expect(@mulWithOverflow(i62, a, b, &res));
-        try expect(res == 0x1fffffffffffffff);
+        ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 0x1fffffffffffffff);
+        try expect(ov[1] == 1);
     }
 
     {
         var a: u64 = 3;
         var b: u64 = 0x5555555555555555;
-        var res: u64 = undefined;
-        try expect(!@mulWithOverflow(u64, a, b, &res));
-        try expect(res == 0xffffffffffffffff);
+        var ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 0xffffffffffffffff);
+        try expect(ov[1] == 0);
 
         b = 0x5555555555555556;
-        try expect(@mulWithOverflow(u64, a, b, &res));
-        try expect(res == 2);
+        ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 2);
+        try expect(ov[1] == 1);
     }
 
     {
         var a: i64 = 3;
         var b: i64 = -0x2aaaaaaaaaaaaaaa;
-        var res: i64 = undefined;
-        try expect(!@mulWithOverflow(i64, a, b, &res));
-        try expect(res == -0x7ffffffffffffffe);
+        var ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == -0x7ffffffffffffffe);
+        try expect(ov[1] == 0);
 
         b = -0x2aaaaaaaaaaaaaab;
-        try expect(@mulWithOverflow(i64, a, b, &res));
-        try expect(res == 0x7fffffffffffffff);
+        ov = @mulWithOverflow(a, b);
+        try expect(ov[0] == 0x7fffffffffffffff);
+        try expect(ov[1] == 1);
     }
 }
 
 test "@subWithOverflow" {
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
 
     {
-        var result: u8 = undefined;
-        try expect(@subWithOverflow(u8, 1, 2, &result));
-        try expect(result == 255);
-        try expect(!@subWithOverflow(u8, 1, 1, &result));
-        try expect(result == 0);
+        var a: u8 = 1;
+        const ov = @subWithOverflow(a, 2);
+        try expect(ov[0] == 255);
+        try expect(ov[1] == 1);
+    }
+    {
+        var a: u8 = 1;
+        const ov = @subWithOverflow(a, 1);
+        try expect(ov[0] == 0);
+        try expect(ov[1] == 0);
+    }
 
+    {
         var a: u8 = 1;
         var b: u8 = 2;
-        try expect(@subWithOverflow(u8, a, b, &result));
-        try expect(result == 255);
+        var ov = @subWithOverflow(a, b);
+        try expect(ov[0] == 255);
+        try expect(ov[1] == 1);
         b = 1;
-        try expect(!@subWithOverflow(u8, a, b, &result));
-        try expect(result == 0);
+        ov = @subWithOverflow(a, b);
+        try expect(ov[0] == 0);
+        try expect(ov[1] == 0);
     }
 
     {
         var a: usize = 6;
         var b: usize = 6;
-        var res: usize = undefined;
-        try expect(!@subWithOverflow(usize, a, b, &res));
-        try expect(res == 0);
+        const ov = @subWithOverflow(a, b);
+        try expect(ov[0] == 0);
+        try expect(ov[1] == 0);
     }
 
     {
         var a: isize = -6;
         var b: isize = -6;
-        var res: isize = undefined;
-        try expect(!@subWithOverflow(isize, a, b, &res));
-        try expect(res == 0);
+        const ov = @subWithOverflow(a, b);
+        try expect(ov[0] == 0);
+        try expect(ov[1] == 0);
     }
 }
 
 test "@shlWithOverflow" {
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
     {
-        var result: u4 = undefined;
         var a: u4 = 2;
         var b: u2 = 1;
-        try expect(!@shlWithOverflow(u4, a, b, &result));
-        try expect(result == 4);
+        var ov = @shlWithOverflow(a, b);
+        try expect(ov[0] == 4);
+        try expect(ov[1] == 0);
 
         b = 3;
-        try expect(@shlWithOverflow(u4, a, b, &result));
-        try expect(result == 0);
+        ov = @shlWithOverflow(a, b);
+        try expect(ov[0] == 0);
+        try expect(ov[1] == 1);
     }
 
     {
-        var result: i9 = undefined;
         var a: i9 = 127;
         var b: u4 = 1;
-        try expect(!@shlWithOverflow(i9, a, b, &result));
-        try expect(result == 254);
+        var ov = @shlWithOverflow(a, b);
+        try expect(ov[0] == 254);
+        try expect(ov[1] == 0);
 
         b = 2;
-        try expect(@shlWithOverflow(i9, a, b, &result));
-        try expect(result == -4);
+        ov = @shlWithOverflow(a, b);
+        try expect(ov[0] == -4);
+        try expect(ov[1] == 1);
     }
 
     {
-        var result: u16 = undefined;
-        try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result));
-        try expect(result == 0b0111111111111000);
-        try expect(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result));
-        try expect(result == 0b1011111111111100);
-
+        const ov = @shlWithOverflow(@as(u16, 0b0010111111111111), 3);
+        try expect(ov[0] == 0b0111111111111000);
+        try expect(ov[1] == 1);
+    }
+    {
+        const ov = @shlWithOverflow(@as(u16, 0b0010111111111111), 2);
+        try expect(ov[0] == 0b1011111111111100);
+        try expect(ov[1] == 0);
+    }
+    {
         var a: u16 = 0b0000_0000_0000_0011;
         var b: u4 = 15;
-        try expect(@shlWithOverflow(u16, a, b, &result));
-        try expect(result == 0b1000_0000_0000_0000);
+        var ov = @shlWithOverflow(a, b);
+        try expect(ov[0] == 0b1000_0000_0000_0000);
+        try expect(ov[1] == 1);
         b = 14;
-        try expect(!@shlWithOverflow(u16, a, b, &result));
-        try expect(result == 0b1100_0000_0000_0000);
+        ov = @shlWithOverflow(a, b);
+        try expect(ov[0] == 0b1100_0000_0000_0000);
+        try expect(ov[1] == 0);
     }
 }
 
 test "overflow arithmetic with u0 values" {
-    var result: u0 = undefined;
-    try expect(!@addWithOverflow(u0, 0, 0, &result));
-    try expect(result == 0);
-    try expect(!@subWithOverflow(u0, 0, 0, &result));
-    try expect(result == 0);
-    try expect(!@mulWithOverflow(u0, 0, 0, &result));
-    try expect(result == 0);
-    try expect(!@shlWithOverflow(u0, 0, 0, &result));
-    try expect(result == 0);
+    {
+        var a: u0 = 0;
+        const ov = @addWithOverflow(a, 0);
+        try expect(ov[1] == 0);
+        try expect(ov[1] == 0);
+    }
+    {
+        var a: u0 = 0;
+        const ov = @subWithOverflow(a, 0);
+        try expect(ov[1] == 0);
+        try expect(ov[1] == 0);
+    }
+    {
+        var a: u0 = 0;
+        const ov = @mulWithOverflow(a, 0);
+        try expect(ov[1] == 0);
+        try expect(ov[1] == 0);
+    }
+    {
+        var a: u0 = 0;
+        const ov = @shlWithOverflow(a, 0);
+        try expect(ov[1] == 0);
+        try expect(ov[1] == 0);
+    }
 }
 
 test "allow signed integer division/remainder when values are comptime-known and positive or exact" {
test/behavior/vector.zig
@@ -963,35 +963,31 @@ test "@addWithOverflow" {
     const S = struct {
         fn doTheTest() !void {
             {
-                var result: @Vector(4, u8) = undefined;
                 var lhs = @Vector(4, u8){ 250, 250, 250, 250 };
                 var rhs = @Vector(4, u8){ 0, 5, 6, 10 };
-                var overflow = @addWithOverflow(@Vector(4, u8), lhs, rhs, &result);
-                var expected: @Vector(4, bool) = .{ false, false, true, true };
+                var overflow = @addWithOverflow(lhs, rhs)[1];
+                var expected: @Vector(4, u1) = .{ 0, 0, 1, 1 };
                 try expectEqual(expected, overflow);
             }
             {
-                var result: @Vector(4, i8) = undefined;
                 var lhs = @Vector(4, i8){ -125, -125, 125, 125 };
                 var rhs = @Vector(4, i8){ -3, -4, 2, 3 };
-                var overflow = @addWithOverflow(@Vector(4, i8), lhs, rhs, &result);
-                var expected: @Vector(4, bool) = .{ false, true, false, true };
+                var overflow = @addWithOverflow(lhs, rhs)[1];
+                var expected: @Vector(4, u1) = .{ 0, 1, 0, 1 };
                 try expectEqual(expected, overflow);
             }
             {
-                var result: @Vector(4, u1) = undefined;
                 var lhs = @Vector(4, u1){ 0, 0, 1, 1 };
                 var rhs = @Vector(4, u1){ 0, 1, 0, 1 };
-                var overflow = @addWithOverflow(@Vector(4, u1), lhs, rhs, &result);
-                var expected: @Vector(4, bool) = .{ false, false, false, true };
+                var overflow = @addWithOverflow(lhs, rhs)[1];
+                var expected: @Vector(4, u1) = .{ 0, 0, 0, 1 };
                 try expectEqual(expected, overflow);
             }
             {
-                var result: @Vector(4, u0) = undefined;
                 var lhs = @Vector(4, u0){ 0, 0, 0, 0 };
                 var rhs = @Vector(4, u0){ 0, 0, 0, 0 };
-                var overflow = @addWithOverflow(@Vector(4, u0), lhs, rhs, &result);
-                var expected: @Vector(4, bool) = .{ false, false, false, false };
+                var overflow = @addWithOverflow(lhs, rhs)[1];
+                var expected: @Vector(4, u1) = .{ 0, 0, 0, 0 };
                 try expectEqual(expected, overflow);
             }
         }
@@ -1010,19 +1006,17 @@ test "@subWithOverflow" {
     const S = struct {
         fn doTheTest() !void {
             {
-                var result: @Vector(2, u8) = undefined;
                 var lhs = @Vector(2, u8){ 5, 5 };
                 var rhs = @Vector(2, u8){ 5, 6 };
-                var overflow = @subWithOverflow(@Vector(2, u8), lhs, rhs, &result);
-                var expected: @Vector(2, bool) = .{ false, true };
+                var overflow = @subWithOverflow(lhs, rhs)[1];
+                var expected: @Vector(2, u1) = .{ 0, 1 };
                 try expectEqual(expected, overflow);
             }
             {
-                var result: @Vector(4, i8) = undefined;
                 var lhs = @Vector(4, i8){ -120, -120, 120, 120 };
                 var rhs = @Vector(4, i8){ 8, 9, -7, -8 };
-                var overflow = @subWithOverflow(@Vector(4, i8), lhs, rhs, &result);
-                var expected: @Vector(4, bool) = .{ false, true, false, true };
+                var overflow = @subWithOverflow(lhs, rhs)[1];
+                var expected: @Vector(4, u1) = .{ 0, 1, 0, 1 };
                 try expectEqual(expected, overflow);
             }
         }
@@ -1040,11 +1034,10 @@ test "@mulWithOverflow" {
 
     const S = struct {
         fn doTheTest() !void {
-            var result: @Vector(4, u8) = undefined;
             var lhs = @Vector(4, u8){ 10, 10, 10, 10 };
             var rhs = @Vector(4, u8){ 25, 26, 0, 30 };
-            var overflow = @mulWithOverflow(@Vector(4, u8), lhs, rhs, &result);
-            var expected: @Vector(4, bool) = .{ false, true, false, true };
+            var overflow = @mulWithOverflow(lhs, rhs)[1];
+            var expected: @Vector(4, u1) = .{ 0, 1, 0, 1 };
             try expectEqual(expected, overflow);
         }
     };
@@ -1062,11 +1055,10 @@ test "@shlWithOverflow" {
 
     const S = struct {
         fn doTheTest() !void {
-            var result: @Vector(4, u8) = undefined;
             var lhs = @Vector(4, u8){ 0, 1, 8, 255 };
             var rhs = @Vector(4, u3){ 7, 7, 7, 7 };
-            var overflow = @shlWithOverflow(@Vector(4, u8), lhs, rhs, &result);
-            var expected: @Vector(4, bool) = .{ false, false, true, true };
+            var overflow = @shlWithOverflow(lhs, rhs)[1];
+            var expected: @Vector(4, u1) = .{ 0, 0, 1, 1 };
             try expectEqual(expected, overflow);
         }
     };