update uses of overflow arithmetic builtins

This commit is contained in:
Veikka Tuominen 2022-12-21 16:40:30 +02:00
parent 54160e7f6a
commit 622311fb9a
29 changed files with 568 additions and 435 deletions

View file

@ -5413,14 +5413,14 @@ pub fn parseU64(buf: []const u8, radix: u8) !u64 {
} }
// x *= radix // x *= radix
if (@mulWithOverflow(u64, x, radix, &x)) { var ov = @mulWithOverflow(x, radix);
return error.Overflow; if (ov[1] != 0) return error.OverFlow;
}
// x += digit // x += digit
if (@addWithOverflow(u64, x, digit, &x)) { ov = @addWithOverflow(ov[0], digit);
return error.Overflow; if (ov[1] != 0) return error.OverFlow;
} x = ov[0];
} }
return x; return x;
@ -5832,14 +5832,16 @@ test "merge error sets" {
{#code_begin|test|inferred_error_sets#} {#code_begin|test|inferred_error_sets#}
// With an inferred error set // With an inferred error set
pub fn add_inferred(comptime T: type, a: T, b: T) !T { pub fn add_inferred(comptime T: type, a: T, b: T) !T {
var answer: T = undefined; const ov = @addWithOverflow(a, b);
return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer; if (ov[1] != 0) return error.Overflow;
return ov[0];
} }
// With an explicit error set // With an explicit error set
pub fn add_explicit(comptime T: type, a: T, b: T) Error!T { pub fn add_explicit(comptime T: type, a: T, b: T) Error!T {
var answer: T = undefined; const ov = @addWithOverflow(a, b);
return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer; if (ov[1] != 0) return error.Overflow;
return ov[0];
} }
const Error = error { const Error = error {
@ -7632,11 +7634,9 @@ test "global assembly" {
</p> </p>
{#header_close#} {#header_close#}
{#header_open|@addWithOverflow#} {#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> <p>
Performs {#syntax#}result.* = a + b{#endsyntax#}. If overflow or underflow occurs, Performs {#syntax#}a + b{#endsyntax#} and returns a tuple with the result and a possible overflow bit.
stores the overflowed bits in {#syntax#}result{#endsyntax#} and returns {#syntax#}true{#endsyntax#}.
If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}.
</p> </p>
{#header_close#} {#header_close#}
{#header_open|@alignCast#} {#header_open|@alignCast#}
@ -8695,11 +8695,9 @@ test "@wasmMemoryGrow" {
{#header_close#} {#header_close#}
{#header_open|@mulWithOverflow#} {#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> <p>
Performs {#syntax#}result.* = a * b{#endsyntax#}. If overflow or underflow occurs, Performs {#syntax#}a * b{#endsyntax#} and returns a tuple with the result and a possible overflow bit.
stores the overflowed bits in {#syntax#}result{#endsyntax#} and returns {#syntax#}true{#endsyntax#}.
If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}.
</p> </p>
{#header_close#} {#header_close#}
@ -8973,15 +8971,13 @@ test "@setRuntimeSafety" {
{#header_close#} {#header_close#}
{#header_open|@shlWithOverflow#} {#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> <p>
Performs {#syntax#}result.* = a << b{#endsyntax#}. If overflow or underflow occurs, Performs {#syntax#}a << b{#endsyntax#} and returns a tuple with the result and a possible overflow bit.
stores the overflowed bits in {#syntax#}result{#endsyntax#} and returns {#syntax#}true{#endsyntax#}.
If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}.
</p> </p>
<p> <p>
The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(@typeInfo(T).Int.bits){#endsyntax#} bits. 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(T).Int.bits{#endsyntax#} is undefined behavior. This is because {#syntax#}shift_amt >= @typeInfo(@TypeOf(a)).Int.bits{#endsyntax#} is undefined behavior.
</p> </p>
{#see_also|@shlExact|@shrExact#} {#see_also|@shlExact|@shrExact#}
{#header_close#} {#header_close#}
@ -9323,11 +9319,9 @@ fn doTheTest() !void {
{#header_close#} {#header_close#}
{#header_open|@subWithOverflow#} {#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> <p>
Performs {#syntax#}result.* = a - b{#endsyntax#}. If overflow or underflow occurs, Performs {#syntax#}a - b{#endsyntax#} and returns a tuple with the result and a possible overflow bit.
stores the overflowed bits in {#syntax#}result{#endsyntax#} and returns {#syntax#}true{#endsyntax#}.
If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}.
</p> </p>
{#header_close#} {#header_close#}
@ -9774,11 +9768,11 @@ const print = @import("std").debug.print;
pub fn main() void { pub fn main() void {
var byte: u8 = 255; var byte: u8 = 255;
var result: u8 = undefined; const ov = @addWithOverflow(byte, 10);
if (@addWithOverflow(u8, byte, 10, &result)) { if (ov[1] != 0) {
print("overflowed result: {}\n", .{result}); print("overflowed result: {}\n", .{ov[0]});
} else { } else {
print("result: {}\n", .{result}); print("result: {}\n", .{ov[0]});
} }
} }
{#code_end#} {#code_end#}

View file

@ -49,14 +49,16 @@ pub fn __trunctfxf2(a: f128) callconv(.C) f80 {
const round_bits = a_abs & round_mask; const round_bits = a_abs & round_mask;
if (round_bits > halfway) { if (round_bits > halfway) {
// Round to nearest // Round to nearest
const carry = @boolToInt(@addWithOverflow(u64, res.fraction, 1, &res.fraction)); const ov = @addWithOverflow(res.fraction, 1);
res.exp += carry; res.fraction = ov[0];
res.fraction |= @as(u64, carry) << 63; // Restore integer bit after carry res.exp += ov[1];
res.fraction |= @as(u64, ov[1]) << 63; // Restore integer bit after carry
} else if (round_bits == halfway) { } else if (round_bits == halfway) {
// Ties to even // Ties to even
const carry = @boolToInt(@addWithOverflow(u64, res.fraction, res.fraction & 1, &res.fraction)); const ov = @addWithOverflow(res.fraction, res.fraction & 1);
res.exp += carry; res.fraction = ov[0];
res.fraction |= @as(u64, carry) << 63; // Restore integer bit after carry 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 if (res.exp == 0) res.fraction &= ~@as(u64, integer_bit); // Remove integer bit for de-normals
} }

View file

@ -172,9 +172,7 @@ test "deflate/inflate" {
defer testing.allocator.free(large_data_chunk); defer testing.allocator.free(large_data_chunk);
// fill with random data // fill with random data
for (large_data_chunk) |_, i| { for (large_data_chunk) |_, i| {
var mul: u8 = @truncate(u8, i); large_data_chunk[i] = @truncate(u8, i) *% @truncate(u8, i);
_ = @mulWithOverflow(u8, mul, mul, &mul);
large_data_chunk[i] = mul;
} }
try testToFromWithLimit(large_data_chunk, limits); try testToFromWithLimit(large_data_chunk, limits);
} }

View file

@ -75,10 +75,10 @@ pub const NonMontgomeryDomainFieldElement = [4]u64;
inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
@setRuntimeSafety(mode == .Debug); @setRuntimeSafety(mode == .Debug);
var t: u64 = undefined; const ov1 = @addWithOverflow(arg2, arg3);
const carry1 = @addWithOverflow(u64, arg2, arg3, &t); const ov2 = @addWithOverflow(ov1[0], arg1);
const carry2 = @addWithOverflow(u64, t, arg1, out1); out1.* = ov2[0];
out2.* = @boolToInt(carry1) | @boolToInt(carry2); out2.* = ov1[1] | ov2[1];
} }
/// The function subborrowxU64 is a subtraction with borrow. /// 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 { inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
@setRuntimeSafety(mode == .Debug); @setRuntimeSafety(mode == .Debug);
var t: u64 = undefined; const ov1 = @subWithOverflow(arg2, arg3);
const carry1 = @subWithOverflow(u64, arg2, arg3, &t); const ov2 = @subWithOverflow(ov1[0], arg1);
const carry2 = @subWithOverflow(u64, t, arg1, out1); out1.* = ov2[0];
out2.* = @boolToInt(carry1) | @boolToInt(carry2); out2.* = ov1[1] | ov2[1];
} }
/// The function mulxU64 is a multiplication, returning the full double-width result. /// The function mulxU64 is a multiplication, returning the full double-width result.

View file

@ -75,10 +75,10 @@ pub const NonMontgomeryDomainFieldElement = [4]u64;
inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
@setRuntimeSafety(mode == .Debug); @setRuntimeSafety(mode == .Debug);
var t: u64 = undefined; const ov1 = @addWithOverflow(arg2, arg3);
const carry1 = @addWithOverflow(u64, arg2, arg3, &t); const ov2 = @addWithOverflow(ov1[0], arg1);
const carry2 = @addWithOverflow(u64, t, arg1, out1); out1.* = ov2[0];
out2.* = @boolToInt(carry1) | @boolToInt(carry2); out2.* = ov1[1] | ov2[1];
} }
/// The function subborrowxU64 is a subtraction with borrow. /// 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 { inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
@setRuntimeSafety(mode == .Debug); @setRuntimeSafety(mode == .Debug);
var t: u64 = undefined; const ov1 = @subWithOverflow(arg2, arg3);
const carry1 = @subWithOverflow(u64, arg2, arg3, &t); const ov2 = @subWithOverflow(ov1[0], arg1);
const carry2 = @subWithOverflow(u64, t, arg1, out1); out1.* = ov2[0];
out2.* = @boolToInt(carry1) | @boolToInt(carry2); out2.* = ov1[1] | ov2[1];
} }
/// The function mulxU64 is a multiplication, returning the full double-width result. /// The function mulxU64 is a multiplication, returning the full double-width result.

View file

@ -44,10 +44,10 @@ pub const NonMontgomeryDomainFieldElement = [6]u64;
inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
@setRuntimeSafety(mode == .Debug); @setRuntimeSafety(mode == .Debug);
var t: u64 = undefined; const ov1 = @addWithOverflow(arg2, arg3);
const carry1 = @addWithOverflow(u64, arg2, arg3, &t); const ov2 = @addWithOverflow(ov1[0], arg1);
const carry2 = @addWithOverflow(u64, t, arg1, out1); out1.* = ov2[0];
out2.* = @boolToInt(carry1) | @boolToInt(carry2); out2.* = ov1[1] | ov2[1];
} }
/// The function subborrowxU64 is a subtraction with borrow. /// 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 { inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
@setRuntimeSafety(mode == .Debug); @setRuntimeSafety(mode == .Debug);
var t: u64 = undefined; const ov1 = @subWithOverflow(arg2, arg3);
const carry1 = @subWithOverflow(u64, arg2, arg3, &t); const ov2 = @subWithOverflow(ov1[0], arg1);
const carry2 = @subWithOverflow(u64, t, arg1, out1); out1.* = ov2[0];
out2.* = @boolToInt(carry1) | @boolToInt(carry2); out2.* = ov1[1] | ov2[1];
} }
/// The function mulxU64 is a multiplication, returning the full double-width result. /// The function mulxU64 is a multiplication, returning the full double-width result.

View file

@ -44,10 +44,10 @@ pub const NonMontgomeryDomainFieldElement = [6]u64;
inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
@setRuntimeSafety(mode == .Debug); @setRuntimeSafety(mode == .Debug);
var t: u64 = undefined; const ov1 = @addWithOverflow(arg2, arg3);
const carry1 = @addWithOverflow(u64, arg2, arg3, &t); const ov2 = @addWithOverflow(ov1[0], arg1);
const carry2 = @addWithOverflow(u64, t, arg1, out1); out1.* = ov2[0];
out2.* = @boolToInt(carry1) | @boolToInt(carry2); out2.* = ov1[1] | ov2[1];
} }
/// The function subborrowxU64 is a subtraction with borrow. /// 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 { inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
@setRuntimeSafety(mode == .Debug); @setRuntimeSafety(mode == .Debug);
var t: u64 = undefined; const ov1 = @subWithOverflow(arg2, arg3);
const carry1 = @subWithOverflow(u64, arg2, arg3, &t); const ov2 = @subWithOverflow(ov1[0], arg1);
const carry2 = @subWithOverflow(u64, t, arg1, out1); out1.* = ov2[0];
out2.* = @boolToInt(carry1) | @boolToInt(carry2); out2.* = ov1[1] | ov2[1];
} }
/// The function mulxU64 is a multiplication, returning the full double-width result. /// The function mulxU64 is a multiplication, returning the full double-width result.

View file

@ -44,10 +44,10 @@ pub const NonMontgomeryDomainFieldElement = [4]u64;
inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
@setRuntimeSafety(mode == .Debug); @setRuntimeSafety(mode == .Debug);
var t: u64 = undefined; const ov1 = @addWithOverflow(arg2, arg3);
const carry1 = @addWithOverflow(u64, arg2, arg3, &t); const ov2 = @addWithOverflow(ov1[0], arg1);
const carry2 = @addWithOverflow(u64, t, arg1, out1); out1.* = ov2[0];
out2.* = @boolToInt(carry1) | @boolToInt(carry2); out2.* = ov1[1] | ov2[1];
} }
/// The function subborrowxU64 is a subtraction with borrow. /// 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 { inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
@setRuntimeSafety(mode == .Debug); @setRuntimeSafety(mode == .Debug);
var t: u64 = undefined; const ov1 = @subWithOverflow(arg2, arg3);
const carry1 = @subWithOverflow(u64, arg2, arg3, &t); const ov2 = @subWithOverflow(ov1[0], arg1);
const carry2 = @subWithOverflow(u64, t, arg1, out1); out1.* = ov2[0];
out2.* = @boolToInt(carry1) | @boolToInt(carry2); out2.* = ov1[1] | ov2[1];
} }
/// The function mulxU64 is a multiplication, returning the full double-width result. /// The function mulxU64 is a multiplication, returning the full double-width result.

View file

@ -44,10 +44,10 @@ pub const NonMontgomeryDomainFieldElement = [4]u64;
inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
@setRuntimeSafety(mode == .Debug); @setRuntimeSafety(mode == .Debug);
var t: u64 = undefined; const ov1 = @addWithOverflow(arg2, arg3);
const carry1 = @addWithOverflow(u64, arg2, arg3, &t); const ov2 = @addWithOverflow(ov1[0], arg1);
const carry2 = @addWithOverflow(u64, t, arg1, out1); out1.* = ov2[0];
out2.* = @boolToInt(carry1) | @boolToInt(carry2); out2.* = ov1[1] | ov2[1];
} }
/// The function subborrowxU64 is a subtraction with borrow. /// 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 { inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void {
@setRuntimeSafety(mode == .Debug); @setRuntimeSafety(mode == .Debug);
var t: u64 = undefined; const ov1 = @subWithOverflow(arg2, arg3);
const carry1 = @subWithOverflow(u64, arg2, arg3, &t); const ov2 = @subWithOverflow(ov1[0], arg1);
const carry2 = @subWithOverflow(u64, t, arg1, out1); out1.* = ov2[0];
out2.* = @boolToInt(carry1) | @boolToInt(carry2); out2.* = ov1[1] | ov2[1];
} }
/// The function mulxU64 is a multiplication, returning the full double-width result. /// The function mulxU64 is a multiplication, returning the full double-width result.

View file

@ -263,7 +263,9 @@ fn SalsaNonVecImpl(comptime rounds: comptime_int) type {
while (j < 64) : (j += 1) { while (j < 64) : (j += 1) {
xout[j] ^= buf[j]; 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) { if (i < in.len) {
salsaCore(x[0..], ctx, true); salsaCore(x[0..], ctx, true);

View file

@ -87,15 +87,19 @@ pub fn timingSafeAdd(comptime T: type, a: []const T, b: []const T, result: []T,
if (endian == .Little) { if (endian == .Little) {
var i: usize = 0; var i: usize = 0;
while (i < len) : (i += 1) { while (i < len) : (i += 1) {
const tmp = @boolToInt(@addWithOverflow(u8, a[i], b[i], &result[i])); const ov1 = @addWithOverflow(a[i], b[i]);
carry = tmp | @boolToInt(@addWithOverflow(u8, result[i], carry, &result[i])); const ov2 = @addWithOverflow(ov1[0], carry);
result[i] = ov2[0];
carry = ov1[1] | ov2[1];
} }
} else { } else {
var i: usize = len; var i: usize = len;
while (i != 0) { while (i != 0) {
i -= 1; i -= 1;
const tmp = @boolToInt(@addWithOverflow(u8, a[i], b[i], &result[i])); const ov1 = @addWithOverflow(a[i], b[i]);
carry = tmp | @boolToInt(@addWithOverflow(u8, result[i], carry, &result[i])); const ov2 = @addWithOverflow(ov1[0], carry);
result[i] = ov2[0];
carry = ov1[1] | ov2[1];
} }
} }
return @bitCast(bool, carry); 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) { if (endian == .Little) {
var i: usize = 0; var i: usize = 0;
while (i < len) : (i += 1) { while (i < len) : (i += 1) {
const tmp = @boolToInt(@subWithOverflow(u8, a[i], b[i], &result[i])); const ov1 = @subWithOverflow(a[i], b[i]);
borrow = tmp | @boolToInt(@subWithOverflow(u8, result[i], borrow, &result[i])); const ov2 = @subWithOverflow(ov1[0], borrow);
result[i] = ov2[0];
borrow = ov1[1] | ov2[1];
} }
} else { } else {
var i: usize = len; var i: usize = len;
while (i != 0) { while (i != 0) {
i -= 1; i -= 1;
const tmp = @boolToInt(@subWithOverflow(u8, a[i], b[i], &result[i])); const ov1 = @subWithOverflow(a[i], b[i]);
borrow = tmp | @boolToInt(@subWithOverflow(u8, result[i], borrow, &result[i])); const ov2 = @subWithOverflow(ov1[0], borrow);
result[i] = ov2[0];
borrow = ov1[1] | ov2[1];
} }
} }
return @bitCast(bool, borrow); return @bitCast(bool, borrow);

View file

@ -789,7 +789,7 @@ pub fn testAllocatorLargeAlignment(base_allocator: mem.Allocator) !void {
const large_align: usize = mem.page_size / 2; const large_align: usize = mem.page_size / 2;
var align_mask: usize = undefined; 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); var slice = try allocator.alignedAlloc(u8, large_align, 500);
try testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr)); try testing.expect(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));

View file

@ -15,11 +15,11 @@ pub fn readULEB128(comptime T: type, reader: anytype) !T {
while (group < max_group) : (group += 1) { while (group < max_group) : (group += 1) {
const byte = try reader.readByte(); 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; if (byte & 0x80 == 0) break;
} else { } else {
return error.Overflow; return error.Overflow;
@ -65,13 +65,13 @@ pub fn readILEB128(comptime T: type, reader: anytype) !T {
while (group < max_group) : (group += 1) { while (group < max_group) : (group += 1) {
const byte = try reader.readByte(); const byte = try reader.readByte();
var temp = @as(U, byte & 0x7f);
const shift = group * 7; 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 // 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 (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 // and all the overflowed bits are 1
const remaining_shift = @intCast(u3, @typeInfo(U).Int.bits - @as(u16, shift)); 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 { } else {
// If we don't overflow and this is the last byte and the number being decoded // 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 // 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_shift = @intCast(u3, @typeInfo(U).Int.bits - @as(u16, shift));
const remaining_bits = @bitCast(i8, byte | 0x80) >> remaining_shift; const remaining_bits = @bitCast(i8, byte | 0x80) >> remaining_shift;
if (remaining_bits != -1) return error.Overflow; if (remaining_bits != -1) return error.Overflow;
} }
} }
value |= temp; value |= ov[0];
if (byte & 0x80 == 0) { if (byte & 0x80 == 0) {
const needs_sign_ext = group + 1 < max_group; const needs_sign_ext = group + 1 < max_group;
if (byte & 0x40 != 0 and needs_sign_ext) { if (byte & 0x40 != 0 and needs_sign_ext) {

View file

@ -468,21 +468,26 @@ test "clamp" {
/// Returns the product of a and b. Returns an error on overflow. /// 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) { pub fn mul(comptime T: type, a: T, b: T) (error{Overflow}!T) {
var answer: T = undefined; if (T == comptime_int) return a * b;
return if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer; 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. /// 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) { pub fn add(comptime T: type, a: T, b: T) (error{Overflow}!T) {
if (T == comptime_int) return a + b; if (T == comptime_int) return a + b;
var answer: T = undefined; const ov = @addWithOverflow(a, b);
return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer; if (ov[1] != 0) return error.Overflow;
return ov[0];
} }
/// Returns a - b, or an error on overflow. /// Returns a - b, or an error on overflow.
pub fn sub(comptime T: type, a: T, b: T) (error{Overflow}!T) { pub fn sub(comptime T: type, a: T, b: T) (error{Overflow}!T) {
var answer: T = undefined; if (T == comptime_int) return a - b;
return if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer; const ov = @subWithOverflow(a, b);
if (ov[1] != 0) return error.Overflow;
return ov[0];
} }
pub fn negate(x: anytype) !@TypeOf(x) { 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 /// Shifts a left by shift_amt. Returns an error on overflow. shift_amt
/// is unsigned. /// is unsigned.
pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T { pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T {
var answer: T = undefined; if (T == comptime_int) return a << shift_amt;
return if (@shlWithOverflow(T, a, shift_amt, &answer)) error.Overflow else answer; const ov = @shlWithOverflow(a, shift_amt);
if (ov[1] != 0) return error.Overflow;
return ov[0];
} }
/// Shifts left. Overflowed bits are truncated. /// Shifts left. Overflowed bits are truncated.

View file

@ -74,42 +74,40 @@ pub fn calcTwosCompLimbCount(bit_count: usize) usize {
/// a + b * c + *carry, sets carry to the overflow bits /// a + b * c + *carry, sets carry to the overflow bits
pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb { pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb {
@setRuntimeSafety(debug_safety); @setRuntimeSafety(debug_safety);
var r1: Limb = undefined;
// r1 = a + *carry // ov1[0] = a + *carry
const c1: Limb = @boolToInt(@addWithOverflow(Limb, a, carry.*, &r1)); const ov1 = @addWithOverflow(a, carry.*);
// r2 = b * c // r2 = b * c
const bc = @as(DoubleLimb, math.mulWide(Limb, b, c)); const bc = @as(DoubleLimb, math.mulWide(Limb, b, c));
const r2 = @truncate(Limb, bc); const r2 = @truncate(Limb, bc);
const c2 = @truncate(Limb, bc >> limb_bits); const c2 = @truncate(Limb, bc >> limb_bits);
// r1 = r1 + r2 // ov2[0] = ov1[0] + r2
const c3: Limb = @boolToInt(@addWithOverflow(Limb, r1, r2, &r1)); const ov2 = @addWithOverflow(ov1[0], r2);
// This never overflows, c1, c3 are either 0 or 1 and if both are 1 then // This never overflows, c1, c3 are either 0 or 1 and if both are 1 then
// c2 is at least <= maxInt(Limb) - 2. // 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 /// a - b * c - *carry, sets carry to the overflow bits
fn subMulLimbWithBorrow(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb { fn subMulLimbWithBorrow(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb {
// r1 = a - *carry // ov1[0] = a - *carry
var r1: Limb = undefined; const ov1 = @subWithOverflow(a, carry.*);
const c1: Limb = @boolToInt(@subWithOverflow(Limb, a, carry.*, &r1));
// r2 = b * c // r2 = b * c
const bc = @as(DoubleLimb, std.math.mulWide(Limb, b, c)); const bc = @as(DoubleLimb, std.math.mulWide(Limb, b, c));
const r2 = @truncate(Limb, bc); const r2 = @truncate(Limb, bc);
const c2 = @truncate(Limb, bc >> limb_bits); const c2 = @truncate(Limb, bc >> limb_bits);
// r1 = r1 - r2 // ov2[0] = ov1[0] - r2
const c3: Limb = @boolToInt(@subWithOverflow(Limb, r1, r2, &r1)); const ov2 = @subWithOverflow(ov1[0], r2);
carry.* = c1 + c2 + c3; carry.* = ov1[1] + c2 + ov2[1];
return r1; return ov2[0];
} }
/// Used to indicate either limit of a 2s-complement integer. /// 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 assert(rma.limbs.ptr != b.limbs.ptr); // illegal aliasing
if (a.limbs.len == 1 and b.limbs.len == 1) { 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.len = 1;
rma.positive = (a.positive == b.positive); rma.positive = (a.positive == b.positive);
return; return;
@ -1836,7 +1836,11 @@ pub const Mutable = struct {
bit_index += @bitSizeOf(Limb); bit_index += @bitSizeOf(Limb);
// 2's complement (bitwise not, then add carry bit) // 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; x.limbs[limb_index] = limb;
} }
@ -1853,7 +1857,11 @@ pub const Mutable = struct {
}; };
// 2's complement (bitwise not, then add carry bit) // 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; x.limbs[limb_index] = limb;
limb_index += 1; limb_index += 1;
@ -2000,7 +2008,9 @@ pub const Const = struct {
// All but the most significant limb. // All but the most significant limb.
for (self.limbs[0 .. self.limbs.len - 1]) |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); sum += @popCount(add_res);
remaining_bits -= limb_bits; // Asserted not to undeflow by fitsInTwosComp 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; var limb: Limb = if (limb_index < x.limbs.len) x.limbs[limb_index] else 0;
// 2's complement (bitwise not, then add carry bit) // 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 // Write one Limb of bits
mem.writePackedInt(Limb, bytes, bit_index + bit_offset, limb, endian); 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; var limb: Limb = if (limb_index < x.limbs.len) x.limbs[limb_index] else 0;
// 2's complement (bitwise not, then add carry bit) // 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 // Write all remaining bits
mem.writeVarPackedInt(bytes, bit_index + bit_offset, bit_count - bit_index, limb, endian); 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; var carry: Limb = 0;
while (i < a.len) : (i += 1) { while (i < a.len) : (i += 1) {
var c: Limb = 0; const ov1 = @addWithOverflow(r[i], a[i]);
c += @boolToInt(@addWithOverflow(Limb, r[i], a[i], &r[i])); r[i] = ov1[0];
c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i])); const ov2 = @addWithOverflow(r[i], carry);
carry = c; r[i] = ov2[0];
carry = @as(Limb, ov1[1]) + ov2[1];
} }
while ((carry != 0) and i < r.len) : (i += 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; j = 0;
while ((carry != 0) and (j < a_hi.len)) : (j += 1) { 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; return carry != 0;
@ -3449,7 +3468,9 @@ fn llmulLimb(comptime op: AccOp, acc: []Limb, y: []const Limb, xi: Limb) bool {
j = 0; j = 0;
while ((borrow != 0) and (j < a_hi.len)) : (j += 1) { 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; return borrow != 0;
@ -3482,14 +3503,17 @@ fn llsubcarry(r: []Limb, a: []const Limb, b: []const Limb) Limb {
var borrow: Limb = 0; var borrow: Limb = 0;
while (i < b.len) : (i += 1) { while (i < b.len) : (i += 1) {
var c: Limb = 0; const ov1 = @subWithOverflow(a[i], b[i]);
c += @boolToInt(@subWithOverflow(Limb, a[i], b[i], &r[i])); r[i] = ov1[0];
c += @boolToInt(@subWithOverflow(Limb, r[i], borrow, &r[i])); const ov2 = @subWithOverflow(r[i], borrow);
borrow = c; r[i] = ov2[0];
borrow = @as(Limb, ov1[1]) + ov2[1];
} }
while (i < a.len) : (i += 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; return borrow;
@ -3512,14 +3536,17 @@ fn lladdcarry(r: []Limb, a: []const Limb, b: []const Limb) Limb {
var carry: Limb = 0; var carry: Limb = 0;
while (i < b.len) : (i += 1) { while (i < b.len) : (i += 1) {
var c: Limb = 0; const ov1 = @addWithOverflow(a[i], b[i]);
c += @boolToInt(@addWithOverflow(Limb, a[i], b[i], &r[i])); r[i] = ov1[0];
c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i])); const ov2 = @addWithOverflow(r[i], carry);
carry = c; r[i] = ov2[0];
carry = @as(Limb, ov1[1]) + ov2[1];
} }
while (i < a.len) : (i += 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; 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; var r_carry: u1 = 1;
while (i < b.len) : (i += 1) { while (i < b.len) : (i += 1) {
var a_limb: Limb = undefined; const ov1 = @subWithOverflow(a[i], a_borrow);
a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb)); a_borrow = ov1[1];
const ov2 = @addWithOverflow(ov1[0] & ~b[i], r_carry);
r[i] = a_limb & ~b[i]; r[i] = ov2[0];
r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); r_carry = ov2[1];
} }
// In order for r_carry to be nonzero at this point, ~b[i] would need to be // 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 // Note, if a_borrow is zero we do not need to compute anything for
// the higher limbs so we can early return here. // the higher limbs so we can early return here.
while (i < a.len and a_borrow == 1) : (i += 1) { 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. 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; var r_carry: u1 = 1;
while (i < b.len) : (i += 1) { while (i < b.len) : (i += 1) {
var b_limb: Limb = undefined; const ov1 = @subWithOverflow(b[i], b_borrow);
b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &b_limb)); b_borrow = ov1[1];
const ov2 = @addWithOverflow(~a[i] & ov1[0], r_carry);
r[i] = ~a[i] & b_limb; r[i] = ov2[0];
r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); r_carry = ov2[1];
} }
// b is at least 1, so this should never underflow. // 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; var r_carry: u1 = 1;
while (i < b.len) : (i += 1) { while (i < b.len) : (i += 1) {
var a_limb: Limb = undefined; const ov1 = @subWithOverflow(a[i], a_borrow);
a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb)); a_borrow = ov1[1];
const ov2 = @subWithOverflow(b[i], b_borrow);
var b_limb: Limb = undefined; b_borrow = ov2[1];
b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &b_limb)); const ov3 = @addWithOverflow(ov1[0] & ov2[0], r_carry);
r[i] = ov3[0];
r[i] = a_limb & b_limb; r_carry = ov3[1];
r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
} }
// b is at least 1, so this should never underflow. // 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; var a_borrow: u1 = 1;
while (i < b.len) : (i += 1) { while (i < b.len) : (i += 1) {
var a_limb: Limb = undefined; const ov = @subWithOverflow(a[i], a_borrow);
a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb)); a_borrow = ov[1];
r[i] = ~a_limb & b[i]; r[i] = ~ov[0] & b[i];
} }
// With b = 0 we have ~(a - 1) & 0 = 0, so the upper bytes are zero. // 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; var b_borrow: u1 = 1;
while (i < b.len) : (i += 1) { while (i < b.len) : (i += 1) {
var a_limb: Limb = undefined; const ov = @subWithOverflow(b[i], b_borrow);
b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &a_limb)); b_borrow = ov[1];
r[i] = a[i] & ~a_limb; r[i] = a[i] & ~ov[0];
} }
assert(b_borrow == 0); // b was 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; var r_carry: u1 = 1;
while (i < b.len) : (i += 1) { while (i < b.len) : (i += 1) {
var a_limb: Limb = undefined; const ov1 = @subWithOverflow(a[i], a_borrow);
a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb)); a_borrow = ov1[1];
const ov2 = @subWithOverflow(b[i], b_borrow);
var b_limb: Limb = undefined; b_borrow = ov2[1];
b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &b_limb)); const ov3 = @addWithOverflow(ov1[0] | ov2[0], r_carry);
r[i] = ov3[0];
r[i] = a_limb | b_limb; r_carry = ov3[1];
r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
} }
// b is at least 1, so this should never underflow. // 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. // With b = 0 and b_borrow = 0 we get (-a - 1) | (-0 - 0) = (-a - 1) | 0 = -a - 1.
while (i < a.len) : (i += 1) { while (i < a.len) : (i += 1) {
a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &r[i])); const ov1 = @subWithOverflow(a[i], a_borrow);
r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); 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. 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); var r_carry = @boolToInt(a_positive != b_positive);
while (i < b.len) : (i += 1) { while (i < b.len) : (i += 1) {
var a_limb: Limb = undefined; const ov1 = @subWithOverflow(a[i], a_borrow);
a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &a_limb)); a_borrow = ov1[1];
const ov2 = @subWithOverflow(b[i], b_borrow);
var b_limb: Limb = undefined; b_borrow = ov2[1];
b_borrow = @boolToInt(@subWithOverflow(Limb, b[i], b_borrow, &b_limb)); const ov3 = @addWithOverflow(ov1[0] ^ ov2[0], r_carry);
r[i] = ov3[0];
r[i] = a_limb ^ b_limb; r_carry = ov3[1];
r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i]));
} }
while (i < a.len) : (i += 1) { while (i < a.len) : (i += 1) {
a_borrow = @boolToInt(@subWithOverflow(Limb, a[i], a_borrow, &r[i])); const ov1 = @subWithOverflow(a[i], a_borrow);
r_carry = @boolToInt(@addWithOverflow(Limb, r[i], r_carry, &r[i])); 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. // 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)]); llsquareBasecase(tmp2, tmp1[0..llnormalize(tmp1)]);
mem.swap([]Limb, &tmp1, &tmp2); mem.swap([]Limb, &tmp1, &tmp2);
// Multiply by a // 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); mem.set(Limb, tmp2, 0);
llmulacc(.add, null, tmp2, tmp1[0..llnormalize(tmp1)], a); llmulacc(.add, null, tmp2, tmp1[0..llnormalize(tmp1)], a);
mem.swap([]Limb, &tmp1, &tmp2); mem.swap([]Limb, &tmp1, &tmp2);

View file

@ -70,22 +70,22 @@ pub fn powi(comptime T: type, x: T, y: T) (error{
while (exp > 1) { while (exp > 1) {
if (exp & 1 == 1) { if (exp & 1 == 1) {
if (@mulWithOverflow(T, acc, base, &acc)) { const ov = @mulWithOverflow(acc, base);
return error.Overflow; if (ov[1] != 0) return error.Overflow;
} acc = ov[0];
} }
exp >>= 1; exp >>= 1;
if (@mulWithOverflow(T, base, base, &base)) { const ov = @mulWithOverflow(base, base);
return error.Overflow; if (ov[1] != 0) return error.Overflow;
} base = ov[0];
} }
if (exp == 1) { if (exp == 1) {
if (@mulWithOverflow(T, acc, base, &acc)) { const ov = @mulWithOverflow(acc, base);
return error.Overflow; if (ov[1] != 0) return error.Overflow;
} acc = ov[0];
} }
return acc; return acc;

View file

@ -3304,13 +3304,13 @@ pub fn alignPointerOffset(ptr: anytype, align_to: usize) ?usize {
// Calculate the aligned base address with an eye out for overflow. // Calculate the aligned base address with an eye out for overflow.
const addr = @ptrToInt(ptr); const addr = @ptrToInt(ptr);
var new_addr: usize = undefined; var ov = @addWithOverflow(addr, align_to - 1);
if (@addWithOverflow(usize, addr, align_to - 1, &new_addr)) return null; if (ov[1] != 0) return null;
new_addr &= ~@as(usize, align_to - 1); ov[0] &= ~@as(usize, align_to - 1);
// The delta is expressed in terms of bytes, turn it into a number of child // The delta is expressed in terms of bytes, turn it into a number of child
// type elements. // type elements.
const delta = new_addr - addr; const delta = ov[0] - addr;
const pointee_size = @sizeOf(info.Pointer.child); const pointee_size = @sizeOf(info.Pointer.child);
if (delta % pointee_size != 0) return null; if (delta % pointee_size != 0) return null;
return delta / pointee_size; return delta / pointee_size;

View file

@ -321,11 +321,15 @@ pub const Ip6Address = extern struct {
if (scope_id) { if (scope_id) {
if (c >= '0' and c <= '9') { if (c >= '0' and c <= '9') {
const digit = c - '0'; 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 { } else {
return error.InvalidCharacter; return error.InvalidCharacter;
@ -377,11 +381,15 @@ pub const Ip6Address = extern struct {
return result; return result;
} else { } else {
const digit = try std.fmt.charToDigit(c, 16); 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; saw_any_digits = true;
} }
@ -492,11 +500,15 @@ pub const Ip6Address = extern struct {
return result; return result;
} else { } else {
const digit = try std.fmt.charToDigit(c, 16); 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; saw_any_digits = true;
} }

View file

@ -1244,7 +1244,7 @@ pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize
var size: i32 = 0; var size: i32 = 0;
const msg_iovlen = @intCast(usize, msg.msg_hdr.msg_iovlen); // kernel side this is treated as unsigned 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| { 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 // batch-send all messages up to the current message
if (next_unsent < i) { if (next_unsent < i) {
const batch_size = i - next_unsent; const batch_size = i - next_unsent;

View file

@ -1023,8 +1023,16 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {
'0'...'9' => byte - '0', '0'...'9' => byte - '0',
else => return error.CorruptPasswordFile, 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) { .ReadGroupId => switch (byte) {
@ -1039,8 +1047,16 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {
'0'...'9' => byte - '0', '0'...'9' => byte - '0',
else => return error.CorruptPasswordFile, 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];
}
}, },
}, },
} }

View file

@ -246,7 +246,9 @@ pub inline fn __builtin_constant_p(expr: anytype) c_int {
return @boolToInt(false); return @boolToInt(false);
} }
pub fn __builtin_mul_overflow(a: anytype, b: anytype, result: *@TypeOf(a, b)) c_int { 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. // __builtin_alloca_with_align is not currently implemented.

View file

@ -151,12 +151,14 @@ pub fn parseNumberLiteral(bytes: []const u8) Result {
special = 0; special = 0;
if (float) continue; if (float) continue;
if (x != 0) if (@mulWithOverflow(u64, x, base, &x)) { if (x != 0) {
overflow = true; const res = @mulWithOverflow(x, base);
}; if (res[1] != 0) overflow = true;
if (@addWithOverflow(u64, x, digit, &x)) { x = res[0];
overflow = true;
} }
const res = @addWithOverflow(x, digit);
if (res[1] != 0) overflow = true;
x = res[0];
} }
if (underscore) return .{ .failure = .{ .trailing_underscore = bytes.len - 1 } }; if (underscore) return .{ .failure = .{ .trailing_underscore = bytes.len - 1 } };
if (special != 0) return .{ .failure = .{ .trailing_special = bytes.len - 1 } }; if (special != 0) return .{ .failure = .{ .trailing_special = bytes.len - 1 } };

View file

@ -1860,9 +1860,7 @@ fn writeHeader(self: *Coff) !void {
} }
pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
// TODO https://github.com/ziglang/zig/issues/1284 return actual_size +| (actual_size / ideal_factor);
return math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch
math.maxInt(@TypeOf(actual_size));
} }
fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 { fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 {

View file

@ -2445,9 +2445,7 @@ fn makeString(self: *Dwarf, bytes: []const u8) !u32 {
} }
fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
// TODO https://github.com/ziglang/zig/issues/1284 return actual_size +| (actual_size / ideal_factor);
return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch
std.math.maxInt(@TypeOf(actual_size));
} }
pub fn flushModule(self: *Dwarf, module: *Module) !void { pub fn flushModule(self: *Dwarf, module: *Module) !void {

View file

@ -3032,9 +3032,7 @@ fn getLDMOption(target: std.Target) ?[]const u8 {
} }
fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
// TODO https://github.com/ziglang/zig/issues/1284 return actual_size +| (actual_size / ideal_factor);
return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch
std.math.maxInt(@TypeOf(actual_size));
} }
// Provide a blueprint of csu (c-runtime startup) objects for supported // Provide a blueprint of csu (c-runtime startup) objects for supported

View file

@ -3772,9 +3772,7 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
} }
pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
// TODO https://github.com/ziglang/zig/issues/1284 return actual_size +| (actual_size / ideal_factor);
return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch
std.math.maxInt(@TypeOf(actual_size));
} }
fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {

View file

@ -489,18 +489,11 @@ test "comptime bitwise operators" {
test "comptime shlWithOverflow" { test "comptime shlWithOverflow" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO 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: { const ct_shifted = @shlWithOverflow(~@as(u64, 0), 16)[0];
var amt = @as(u64, 0); var a = ~@as(u64, 0);
_ = @shlWithOverflow(u64, ~@as(u64, 0), 16, &amt); const rt_shifted = @shlWithOverflow(a, 16)[0];
break :amt amt;
};
const rt_shifted: u64 = amt: {
var amt = @as(u64, 0);
_ = @shlWithOverflow(u64, ~@as(u64, 0), 16, &amt);
break :amt amt;
};
try expect(ct_shifted == rt_shifted); try expect(ct_shifted == rt_shifted);
} }

View file

@ -534,6 +534,7 @@ fn testUnsignedNegationWrappingEval(x: u16) !void {
test "negation wrapping" { test "negation wrapping" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO 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 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
try expectEqual(@as(u1, 1), negateWrap(u1, 1)); try expectEqual(@as(u1, 1), negateWrap(u1, 1));
@ -634,42 +635,53 @@ test "128-bit multiplication" {
} }
test "@addWithOverflow" { 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 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
{ {
var result: u8 = undefined; var a: u8 = 250;
try expect(@addWithOverflow(u8, 250, 100, &result)); const ov = @addWithOverflow(a, 100);
try expect(result == 94); try expect(ov[0] == 94);
try expect(!@addWithOverflow(u8, 100, 150, &result)); try expect(ov[1] == 1);
try expect(result == 250); }
{
var a: u8 = 100;
const ov = @addWithOverflow(a, 150);
try expect(ov[0] == 250);
try expect(ov[1] == 0);
}
{
var a: u8 = 200; var a: u8 = 200;
var b: u8 = 99; var b: u8 = 99;
try expect(@addWithOverflow(u8, a, b, &result)); var ov = @addWithOverflow(a, b);
try expect(result == 43); try expect(ov[0] == 43);
try expect(ov[1] == 1);
b = 55; b = 55;
try expect(!@addWithOverflow(u8, a, b, &result)); ov = @addWithOverflow(a, b);
try expect(result == 255); try expect(ov[0] == 255);
try expect(ov[1] == 0);
} }
{ {
var a: usize = 6; var a: usize = 6;
var b: usize = 6; var b: usize = 6;
var res: usize = undefined; const ov = @addWithOverflow(a, b);
try expect(!@addWithOverflow(usize, a, b, &res)); try expect(ov[0] == 12);
try expect(res == 12); try expect(ov[1] == 0);
} }
{ {
var a: isize = -6; var a: isize = -6;
var b: isize = -6; var b: isize = -6;
var res: isize = undefined; const ov = @addWithOverflow(a, b);
try expect(!@addWithOverflow(isize, a, b, &res)); try expect(ov[0] == -12);
try expect(res == -12); try expect(ov[1] == 0);
} }
} }
test "small int addition" { test "small int addition" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
var x: u2 = 0; var x: u2 = 0;
@ -684,180 +696,206 @@ test "small int addition" {
x += 1; x += 1;
try expect(x == 3); try expect(x == 3);
var result: @TypeOf(x) = 3; const ov = @addWithOverflow(x, 1);
try expect(@addWithOverflow(@TypeOf(x), x, 1, &result)); try expect(ov[0] == 0);
try expect(ov[1] == 1);
try expect(result == 0);
} }
test "basic @mulWithOverflow" { test "basic @mulWithOverflow" {
var result: u8 = undefined; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
try expect(@mulWithOverflow(u8, 86, 3, &result)); if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
try expect(result == 2); if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
try expect(!@mulWithOverflow(u8, 85, 3, &result));
try expect(result == 255); {
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 a: u8 = 123;
var b: u8 = 2; var b: u8 = 2;
try expect(!@mulWithOverflow(u8, a, b, &result)); var ov = @mulWithOverflow(a, b);
try expect(result == 246); try expect(ov[0] == 246);
try expect(ov[1] == 0);
b = 4; b = 4;
try expect(@mulWithOverflow(u8, a, b, &result)); ov = @mulWithOverflow(a, b);
try expect(result == 236); 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" { 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_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
{ {
var a: u5 = 3; var a: u5 = 3;
var b: u5 = 10; var b: u5 = 10;
var res: u5 = undefined; var ov = @mulWithOverflow(a, b);
try expect(!@mulWithOverflow(u5, a, b, &res)); try expect(ov[0] == 30);
try expect(res == 30); try expect(ov[1] == 0);
b = 11; b = 11;
try expect(@mulWithOverflow(u5, a, b, &res)); ov = @mulWithOverflow(a, b);
try expect(res == 1); try expect(ov[0] == 1);
try expect(ov[1] == 1);
} }
{ {
var a: i5 = 3; var a: i5 = 3;
var b: i5 = -5; var b: i5 = -5;
var res: i5 = undefined; var ov = @mulWithOverflow(a, b);
try expect(!@mulWithOverflow(i5, a, b, &res)); try expect(ov[0] == -15);
try expect(res == -15); try expect(ov[1] == 0);
b = -6; b = -6;
try expect(@mulWithOverflow(i5, a, b, &res)); ov = @mulWithOverflow(a, b);
try expect(res == 14); try expect(ov[0] == 14);
try expect(ov[1] == 1);
} }
{ {
var a: u8 = 3; var a: u8 = 3;
var b: u8 = 85; var b: u8 = 85;
var res: u8 = undefined;
try expect(!@mulWithOverflow(u8, a, b, &res)); var ov = @mulWithOverflow(a, b);
try expect(res == 255); try expect(ov[0] == 255);
try expect(ov[1] == 0);
b = 86; b = 86;
try expect(@mulWithOverflow(u8, a, b, &res)); ov = @mulWithOverflow(a, b);
try expect(res == 2); try expect(ov[0] == 2);
try expect(ov[1] == 1);
} }
{ {
var a: i8 = 3; var a: i8 = 3;
var b: i8 = -42; var b: i8 = -42;
var res: i8 = undefined; var ov = @mulWithOverflow(a, b);
try expect(!@mulWithOverflow(i8, a, b, &res)); try expect(ov[0] == -126);
try expect(res == -126); try expect(ov[1] == 0);
b = -43; b = -43;
try expect(@mulWithOverflow(i8, a, b, &res)); ov = @mulWithOverflow(a, b);
try expect(res == 127); try expect(ov[0] == 127);
try expect(ov[1] == 1);
} }
{ {
var a: u14 = 3; var a: u14 = 3;
var b: u14 = 0x1555; var b: u14 = 0x1555;
var res: u14 = undefined; var ov = @mulWithOverflow(a, b);
try expect(!@mulWithOverflow(u14, a, b, &res)); try expect(ov[0] == 0x3fff);
try expect(res == 0x3fff); try expect(ov[1] == 0);
b = 0x1556; b = 0x1556;
try expect(@mulWithOverflow(u14, a, b, &res)); ov = @mulWithOverflow(a, b);
try expect(res == 2); try expect(ov[0] == 2);
try expect(ov[1] == 1);
} }
{ {
var a: i14 = 3; var a: i14 = 3;
var b: i14 = -0xaaa; var b: i14 = -0xaaa;
var res: i14 = undefined; var ov = @mulWithOverflow(a, b);
try expect(!@mulWithOverflow(i14, a, b, &res)); try expect(ov[0] == -0x1ffe);
try expect(res == -0x1ffe); try expect(ov[1] == 0);
b = -0xaab; b = -0xaab;
try expect(@mulWithOverflow(i14, a, b, &res)); ov = @mulWithOverflow(a, b);
try expect(res == 0x1fff); try expect(ov[0] == 0x1fff);
} }
{ {
var a: u16 = 3; var a: u16 = 3;
var b: u16 = 0x5555; var b: u16 = 0x5555;
var res: u16 = undefined; var ov = @mulWithOverflow(a, b);
try expect(!@mulWithOverflow(u16, a, b, &res)); try expect(ov[0] == 0xffff);
try expect(res == 0xffff); try expect(ov[1] == 0);
b = 0x5556; b = 0x5556;
try expect(@mulWithOverflow(u16, a, b, &res)); ov = @mulWithOverflow(a, b);
try expect(res == 2); try expect(ov[0] == 2);
try expect(ov[1] == 1);
} }
{ {
var a: i16 = 3; var a: i16 = 3;
var b: i16 = -0x2aaa; var b: i16 = -0x2aaa;
var res: i16 = undefined; var ov = @mulWithOverflow(a, b);
try expect(!@mulWithOverflow(i16, a, b, &res)); try expect(ov[0] == -0x7ffe);
try expect(res == -0x7ffe); try expect(ov[1] == 0);
b = -0x2aab; b = -0x2aab;
try expect(@mulWithOverflow(i16, a, b, &res)); ov = @mulWithOverflow(a, b);
try expect(res == 0x7fff); try expect(ov[0] == 0x7fff);
try expect(ov[1] == 1);
} }
{ {
var a: u30 = 3; var a: u30 = 3;
var b: u30 = 0x15555555; var b: u30 = 0x15555555;
var res: u30 = undefined; var ov = @mulWithOverflow(a, b);
try expect(!@mulWithOverflow(u30, a, b, &res)); try expect(ov[0] == 0x3fffffff);
try expect(res == 0x3fffffff); try expect(ov[1] == 0);
b = 0x15555556; b = 0x15555556;
try expect(@mulWithOverflow(u30, a, b, &res)); ov = @mulWithOverflow(a, b);
try expect(res == 2); try expect(ov[0] == 2);
try expect(ov[1] == 1);
} }
{ {
var a: i30 = 3; var a: i30 = 3;
var b: i30 = -0xaaaaaaa; var b: i30 = -0xaaaaaaa;
var res: i30 = undefined; var ov = @mulWithOverflow(a, b);
try expect(!@mulWithOverflow(i30, a, b, &res)); try expect(ov[0] == -0x1ffffffe);
try expect(res == -0x1ffffffe); try expect(ov[1] == 0);
b = -0xaaaaaab; b = -0xaaaaaab;
try expect(@mulWithOverflow(i30, a, b, &res)); ov = @mulWithOverflow(a, b);
try expect(res == 0x1fffffff); try expect(ov[0] == 0x1fffffff);
try expect(ov[1] == 1);
} }
{ {
var a: u32 = 3; var a: u32 = 3;
var b: u32 = 0x55555555; var b: u32 = 0x55555555;
var res: u32 = undefined; var ov = @mulWithOverflow(a, b);
try expect(!@mulWithOverflow(u32, a, b, &res)); try expect(ov[0] == 0xffffffff);
try expect(res == 0xffffffff); try expect(ov[1] == 0);
b = 0x55555556; b = 0x55555556;
try expect(@mulWithOverflow(u32, a, b, &res)); ov = @mulWithOverflow(a, b);
try expect(res == 2); try expect(ov[0] == 2);
try expect(ov[1] == 1);
} }
{ {
var a: i32 = 3; var a: i32 = 3;
var b: i32 = -0x2aaaaaaa; var b: i32 = -0x2aaaaaaa;
var res: i32 = undefined; var ov = @mulWithOverflow(a, b);
try expect(!@mulWithOverflow(i32, a, b, &res)); try expect(ov[0] == -0x7ffffffe);
try expect(res == -0x7ffffffe); try expect(ov[1] == 0);
b = -0x2aaaaaab; b = -0x2aaaaaab;
try expect(@mulWithOverflow(i32, a, b, &res)); ov = @mulWithOverflow(a, b);
try expect(res == 0x7fffffff); try expect(ov[0] == 0x7fffffff);
try expect(ov[1] == 1);
} }
} }
test "@mulWithOverflow bitsize > 32" { 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_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) 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 a: u62 = 3;
var b: u62 = 0x1555555555555555; var b: u62 = 0x1555555555555555;
var res: u62 = undefined; var ov = @mulWithOverflow(a, b);
try expect(!@mulWithOverflow(u62, a, b, &res)); try expect(ov[0] == 0x3fffffffffffffff);
try expect(res == 0x3fffffffffffffff); try expect(ov[1] == 0);
b = 0x1555555555555556; b = 0x1555555555555556;
try expect(@mulWithOverflow(u62, a, b, &res)); ov = @mulWithOverflow(a, b);
try expect(res == 2); try expect(ov[0] == 2);
try expect(ov[1] == 1);
} }
{ {
var a: i62 = 3; var a: i62 = 3;
var b: i62 = -0xaaaaaaaaaaaaaaa; var b: i62 = -0xaaaaaaaaaaaaaaa;
var res: i62 = undefined; var ov = @mulWithOverflow(a, b);
try expect(!@mulWithOverflow(i62, a, b, &res)); try expect(ov[0] == -0x1ffffffffffffffe);
try expect(res == -0x1ffffffffffffffe); try expect(ov[1] == 0);
b = -0xaaaaaaaaaaaaaab; b = -0xaaaaaaaaaaaaaab;
try expect(@mulWithOverflow(i62, a, b, &res)); ov = @mulWithOverflow(a, b);
try expect(res == 0x1fffffffffffffff); try expect(ov[0] == 0x1fffffffffffffff);
try expect(ov[1] == 1);
} }
{ {
var a: u64 = 3; var a: u64 = 3;
var b: u64 = 0x5555555555555555; var b: u64 = 0x5555555555555555;
var res: u64 = undefined; var ov = @mulWithOverflow(a, b);
try expect(!@mulWithOverflow(u64, a, b, &res)); try expect(ov[0] == 0xffffffffffffffff);
try expect(res == 0xffffffffffffffff); try expect(ov[1] == 0);
b = 0x5555555555555556; b = 0x5555555555555556;
try expect(@mulWithOverflow(u64, a, b, &res)); ov = @mulWithOverflow(a, b);
try expect(res == 2); try expect(ov[0] == 2);
try expect(ov[1] == 1);
} }
{ {
var a: i64 = 3; var a: i64 = 3;
var b: i64 = -0x2aaaaaaaaaaaaaaa; var b: i64 = -0x2aaaaaaaaaaaaaaa;
var res: i64 = undefined; var ov = @mulWithOverflow(a, b);
try expect(!@mulWithOverflow(i64, a, b, &res)); try expect(ov[0] == -0x7ffffffffffffffe);
try expect(res == -0x7ffffffffffffffe); try expect(ov[1] == 0);
b = -0x2aaaaaaaaaaaaaab; b = -0x2aaaaaaaaaaaaaab;
try expect(@mulWithOverflow(i64, a, b, &res)); ov = @mulWithOverflow(a, b);
try expect(res == 0x7fffffffffffffff); try expect(ov[0] == 0x7fffffffffffffff);
try expect(ov[1] == 1);
} }
} }
test "@subWithOverflow" { 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 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
{ {
var result: u8 = undefined; var a: u8 = 1;
try expect(@subWithOverflow(u8, 1, 2, &result)); const ov = @subWithOverflow(a, 2);
try expect(result == 255); try expect(ov[0] == 255);
try expect(!@subWithOverflow(u8, 1, 1, &result)); try expect(ov[1] == 1);
try expect(result == 0); }
{
var a: u8 = 1;
const ov = @subWithOverflow(a, 1);
try expect(ov[0] == 0);
try expect(ov[1] == 0);
}
{
var a: u8 = 1; var a: u8 = 1;
var b: u8 = 2; var b: u8 = 2;
try expect(@subWithOverflow(u8, a, b, &result)); var ov = @subWithOverflow(a, b);
try expect(result == 255); try expect(ov[0] == 255);
try expect(ov[1] == 1);
b = 1; b = 1;
try expect(!@subWithOverflow(u8, a, b, &result)); ov = @subWithOverflow(a, b);
try expect(result == 0); try expect(ov[0] == 0);
try expect(ov[1] == 0);
} }
{ {
var a: usize = 6; var a: usize = 6;
var b: usize = 6; var b: usize = 6;
var res: usize = undefined; const ov = @subWithOverflow(a, b);
try expect(!@subWithOverflow(usize, a, b, &res)); try expect(ov[0] == 0);
try expect(res == 0); try expect(ov[1] == 0);
} }
{ {
var a: isize = -6; var a: isize = -6;
var b: isize = -6; var b: isize = -6;
var res: isize = undefined; const ov = @subWithOverflow(a, b);
try expect(!@subWithOverflow(isize, a, b, &res)); try expect(ov[0] == 0);
try expect(res == 0); try expect(ov[1] == 0);
} }
} }
test "@shlWithOverflow" { 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 a: u4 = 2;
var b: u2 = 1; var b: u2 = 1;
try expect(!@shlWithOverflow(u4, a, b, &result)); var ov = @shlWithOverflow(a, b);
try expect(result == 4); try expect(ov[0] == 4);
try expect(ov[1] == 0);
b = 3; b = 3;
try expect(@shlWithOverflow(u4, a, b, &result)); ov = @shlWithOverflow(a, b);
try expect(result == 0); try expect(ov[0] == 0);
try expect(ov[1] == 1);
} }
{ {
var result: i9 = undefined;
var a: i9 = 127; var a: i9 = 127;
var b: u4 = 1; var b: u4 = 1;
try expect(!@shlWithOverflow(i9, a, b, &result)); var ov = @shlWithOverflow(a, b);
try expect(result == 254); try expect(ov[0] == 254);
try expect(ov[1] == 0);
b = 2; b = 2;
try expect(@shlWithOverflow(i9, a, b, &result)); ov = @shlWithOverflow(a, b);
try expect(result == -4); try expect(ov[0] == -4);
try expect(ov[1] == 1);
} }
{ {
var result: u16 = undefined; const ov = @shlWithOverflow(@as(u16, 0b0010111111111111), 3);
try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); try expect(ov[0] == 0b0111111111111000);
try expect(result == 0b0111111111111000); try expect(ov[1] == 1);
try expect(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result)); }
try expect(result == 0b1011111111111100); {
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 a: u16 = 0b0000_0000_0000_0011;
var b: u4 = 15; var b: u4 = 15;
try expect(@shlWithOverflow(u16, a, b, &result)); var ov = @shlWithOverflow(a, b);
try expect(result == 0b1000_0000_0000_0000); try expect(ov[0] == 0b1000_0000_0000_0000);
try expect(ov[1] == 1);
b = 14; b = 14;
try expect(!@shlWithOverflow(u16, a, b, &result)); ov = @shlWithOverflow(a, b);
try expect(result == 0b1100_0000_0000_0000); try expect(ov[0] == 0b1100_0000_0000_0000);
try expect(ov[1] == 0);
} }
} }
test "overflow arithmetic with u0 values" { test "overflow arithmetic with u0 values" {
var result: u0 = undefined; {
try expect(!@addWithOverflow(u0, 0, 0, &result)); var a: u0 = 0;
try expect(result == 0); const ov = @addWithOverflow(a, 0);
try expect(!@subWithOverflow(u0, 0, 0, &result)); try expect(ov[1] == 0);
try expect(result == 0); try expect(ov[1] == 0);
try expect(!@mulWithOverflow(u0, 0, 0, &result)); }
try expect(result == 0); {
try expect(!@shlWithOverflow(u0, 0, 0, &result)); var a: u0 = 0;
try expect(result == 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 "allow signed integer division/remainder when values are comptime-known and positive or exact" {

View file

@ -963,35 +963,31 @@ test "@addWithOverflow" {
const S = struct { const S = struct {
fn doTheTest() !void { fn doTheTest() !void {
{ {
var result: @Vector(4, u8) = undefined;
var lhs = @Vector(4, u8){ 250, 250, 250, 250 }; var lhs = @Vector(4, u8){ 250, 250, 250, 250 };
var rhs = @Vector(4, u8){ 0, 5, 6, 10 }; var rhs = @Vector(4, u8){ 0, 5, 6, 10 };
var overflow = @addWithOverflow(@Vector(4, u8), lhs, rhs, &result); var overflow = @addWithOverflow(lhs, rhs)[1];
var expected: @Vector(4, bool) = .{ false, false, true, true }; var expected: @Vector(4, u1) = .{ 0, 0, 1, 1 };
try expectEqual(expected, overflow); try expectEqual(expected, overflow);
} }
{ {
var result: @Vector(4, i8) = undefined;
var lhs = @Vector(4, i8){ -125, -125, 125, 125 }; var lhs = @Vector(4, i8){ -125, -125, 125, 125 };
var rhs = @Vector(4, i8){ -3, -4, 2, 3 }; var rhs = @Vector(4, i8){ -3, -4, 2, 3 };
var overflow = @addWithOverflow(@Vector(4, i8), lhs, rhs, &result); var overflow = @addWithOverflow(lhs, rhs)[1];
var expected: @Vector(4, bool) = .{ false, true, false, true }; var expected: @Vector(4, u1) = .{ 0, 1, 0, 1 };
try expectEqual(expected, overflow); try expectEqual(expected, overflow);
} }
{ {
var result: @Vector(4, u1) = undefined;
var lhs = @Vector(4, u1){ 0, 0, 1, 1 }; var lhs = @Vector(4, u1){ 0, 0, 1, 1 };
var rhs = @Vector(4, u1){ 0, 1, 0, 1 }; var rhs = @Vector(4, u1){ 0, 1, 0, 1 };
var overflow = @addWithOverflow(@Vector(4, u1), lhs, rhs, &result); var overflow = @addWithOverflow(lhs, rhs)[1];
var expected: @Vector(4, bool) = .{ false, false, false, true }; var expected: @Vector(4, u1) = .{ 0, 0, 0, 1 };
try expectEqual(expected, overflow); try expectEqual(expected, overflow);
} }
{ {
var result: @Vector(4, u0) = undefined;
var lhs = @Vector(4, u0){ 0, 0, 0, 0 }; var lhs = @Vector(4, u0){ 0, 0, 0, 0 };
var rhs = @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 overflow = @addWithOverflow(lhs, rhs)[1];
var expected: @Vector(4, bool) = .{ false, false, false, false }; var expected: @Vector(4, u1) = .{ 0, 0, 0, 0 };
try expectEqual(expected, overflow); try expectEqual(expected, overflow);
} }
} }
@ -1010,19 +1006,17 @@ test "@subWithOverflow" {
const S = struct { const S = struct {
fn doTheTest() !void { fn doTheTest() !void {
{ {
var result: @Vector(2, u8) = undefined;
var lhs = @Vector(2, u8){ 5, 5 }; var lhs = @Vector(2, u8){ 5, 5 };
var rhs = @Vector(2, u8){ 5, 6 }; var rhs = @Vector(2, u8){ 5, 6 };
var overflow = @subWithOverflow(@Vector(2, u8), lhs, rhs, &result); var overflow = @subWithOverflow(lhs, rhs)[1];
var expected: @Vector(2, bool) = .{ false, true }; var expected: @Vector(2, u1) = .{ 0, 1 };
try expectEqual(expected, overflow); try expectEqual(expected, overflow);
} }
{ {
var result: @Vector(4, i8) = undefined;
var lhs = @Vector(4, i8){ -120, -120, 120, 120 }; var lhs = @Vector(4, i8){ -120, -120, 120, 120 };
var rhs = @Vector(4, i8){ 8, 9, -7, -8 }; var rhs = @Vector(4, i8){ 8, 9, -7, -8 };
var overflow = @subWithOverflow(@Vector(4, i8), lhs, rhs, &result); var overflow = @subWithOverflow(lhs, rhs)[1];
var expected: @Vector(4, bool) = .{ false, true, false, true }; var expected: @Vector(4, u1) = .{ 0, 1, 0, 1 };
try expectEqual(expected, overflow); try expectEqual(expected, overflow);
} }
} }
@ -1040,11 +1034,10 @@ test "@mulWithOverflow" {
const S = struct { const S = struct {
fn doTheTest() !void { fn doTheTest() !void {
var result: @Vector(4, u8) = undefined;
var lhs = @Vector(4, u8){ 10, 10, 10, 10 }; var lhs = @Vector(4, u8){ 10, 10, 10, 10 };
var rhs = @Vector(4, u8){ 25, 26, 0, 30 }; var rhs = @Vector(4, u8){ 25, 26, 0, 30 };
var overflow = @mulWithOverflow(@Vector(4, u8), lhs, rhs, &result); var overflow = @mulWithOverflow(lhs, rhs)[1];
var expected: @Vector(4, bool) = .{ false, true, false, true }; var expected: @Vector(4, u1) = .{ 0, 1, 0, 1 };
try expectEqual(expected, overflow); try expectEqual(expected, overflow);
} }
}; };
@ -1062,11 +1055,10 @@ test "@shlWithOverflow" {
const S = struct { const S = struct {
fn doTheTest() !void { fn doTheTest() !void {
var result: @Vector(4, u8) = undefined;
var lhs = @Vector(4, u8){ 0, 1, 8, 255 }; var lhs = @Vector(4, u8){ 0, 1, 8, 255 };
var rhs = @Vector(4, u3){ 7, 7, 7, 7 }; var rhs = @Vector(4, u3){ 7, 7, 7, 7 };
var overflow = @shlWithOverflow(@Vector(4, u8), lhs, rhs, &result); var overflow = @shlWithOverflow(lhs, rhs)[1];
var expected: @Vector(4, bool) = .{ false, false, true, true }; var expected: @Vector(4, u1) = .{ 0, 0, 1, 1 };
try expectEqual(expected, overflow); try expectEqual(expected, overflow);
} }
}; };