diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index da21745cce..286237aa7b 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -106,6 +106,12 @@ comptime { } // Integral arithmetic which returns if overflow + const __addosi4 = @import("compiler_rt/addo.zig").__addosi4; + @export(__addosi4, .{ .name = "__addosi4", .linkage = linkage }); + const __addodi4 = @import("compiler_rt/addo.zig").__addodi4; + @export(__addodi4, .{ .name = "__addodi4", .linkage = linkage }); + const __addoti4 = @import("compiler_rt/addo.zig").__addoti4; + @export(__addoti4, .{ .name = "__addoti4", .linkage = linkage }); const __mulosi4 = @import("compiler_rt/mulo.zig").__mulosi4; @export(__mulosi4, .{ .name = "__mulosi4", .linkage = linkage }); const __mulodi4 = @import("compiler_rt/mulo.zig").__mulodi4; diff --git a/lib/std/special/compiler_rt/addo.zig b/lib/std/special/compiler_rt/addo.zig new file mode 100644 index 0000000000..966c74cb8e --- /dev/null +++ b/lib/std/special/compiler_rt/addo.zig @@ -0,0 +1,38 @@ +const builtin = @import("builtin"); + +// addo - add overflow +// * return a+%b. +// * return if a+b overflows => 1 else => 0 +// - addoXi4_generic as default + +inline fn addoXi4_generic(comptime ST: type, a: ST, b: ST, overflow: *c_int) ST { + @setRuntimeSafety(builtin.is_test); + overflow.* = 0; + var sum: ST = a +% b; + // Hackers Delight: section Overflow Detection, subsection Signed Add/Subtract + // Let sum = a +% b == a + b + carry == wraparound addition. + // Overflow in a+b+carry occurs, iff a and b have opposite signs + // and the sign of a+b+carry is the same as a (or equivalently b). + // Slower routine: res = ~(a ^ b) & ((sum ^ a) + // Faster routine: res = (sum ^ a) & (sum ^ b) + // Oerflow occured, iff (res < 0) + if (((sum ^ a) & (sum ^ b)) < 0) + overflow.* = 1; + return sum; +} + +pub fn __addosi4(a: i32, b: i32, overflow: *c_int) callconv(.C) i32 { + return addoXi4_generic(i32, a, b, overflow); +} +pub fn __addodi4(a: i64, b: i64, overflow: *c_int) callconv(.C) i64 { + return addoXi4_generic(i64, a, b, overflow); +} +pub fn __addoti4(a: i128, b: i128, overflow: *c_int) callconv(.C) i128 { + return addoXi4_generic(i128, a, b, overflow); +} + +test { + _ = @import("addosi4_test.zig"); + _ = @import("addodi4_test.zig"); + _ = @import("addoti4_test.zig"); +} diff --git a/lib/std/special/compiler_rt/addodi4_test.zig b/lib/std/special/compiler_rt/addodi4_test.zig new file mode 100644 index 0000000000..f70a80a5b2 --- /dev/null +++ b/lib/std/special/compiler_rt/addodi4_test.zig @@ -0,0 +1,77 @@ +const addv = @import("addo.zig"); +const std = @import("std"); +const testing = std.testing; +const math = std.math; + +fn test__addodi4(a: i64, b: i64) !void { + var result_ov: c_int = undefined; + var expected_ov: c_int = undefined; + var result = addv.__addodi4(a, b, &result_ov); + var expected: i64 = simple_addodi4(a, b, &expected_ov); + try testing.expectEqual(expected, result); + try testing.expectEqual(expected_ov, result_ov); +} + +fn simple_addodi4(a: i64, b: i64, overflow: *c_int) i64 { + overflow.* = 0; + const min: i64 = math.minInt(i64); + const max: i64 = math.maxInt(i64); + if (((a > 0) and (b > max - a)) or + ((a < 0) and (b < min - a))) + overflow.* = 1; + return a +% b; +} + +test "addodi4" { + const min: i64 = math.minInt(i64); + const max: i64 = math.maxInt(i64); + var i: i64 = 1; + while (i < max) : (i *|= 2) { + try test__addodi4(i, i); + try test__addodi4(-i, -i); + try test__addodi4(i, -i); + try test__addodi4(-i, i); + } + + // edge cases + // 0 + 0 = 0 + // MIN + MIN overflow + // MAX + MAX overflow + // 0 + MIN MIN + // 0 + MAX MAX + // MIN + 0 MIN + // MAX + 0 MAX + // MIN + MAX -1 + // MAX + MIN -1 + try test__addodi4(0, 0); + try test__addodi4(min, min); + try test__addodi4(max, max); + try test__addodi4(0, min); + try test__addodi4(0, max); + try test__addodi4(min, 0); + try test__addodi4(max, 0); + try test__addodi4(min, max); + try test__addodi4(max, min); + + // derived edge cases + // MIN+1 + MIN overflow + // MAX-1 + MAX overflow + // 1 + MIN = MIN+1 + // -1 + MIN overflow + // -1 + MAX = MAX-1 + // +1 + MAX overflow + // MIN + 1 = MIN+1 + // MIN + -1 overflow + // MAX + 1 overflow + // MAX + -1 = MAX-1 + try test__addodi4(min + 1, min); + try test__addodi4(max - 1, max); + try test__addodi4(1, min); + try test__addodi4(-1, min); + try test__addodi4(-1, max); + try test__addodi4(1, max); + try test__addodi4(min, 1); + try test__addodi4(min, -1); + try test__addodi4(max, -1); + try test__addodi4(max, 1); +} diff --git a/lib/std/special/compiler_rt/addosi4_test.zig b/lib/std/special/compiler_rt/addosi4_test.zig new file mode 100644 index 0000000000..a8f81d70d1 --- /dev/null +++ b/lib/std/special/compiler_rt/addosi4_test.zig @@ -0,0 +1,78 @@ +const addv = @import("addo.zig"); +const testing = @import("std").testing; + +fn test__addosi4(a: i32, b: i32) !void { + var result_ov: c_int = undefined; + var expected_ov: c_int = undefined; + var result = addv.__addosi4(a, b, &result_ov); + var expected: i32 = simple_addosi4(a, b, &expected_ov); + try testing.expectEqual(expected, result); + try testing.expectEqual(expected_ov, result_ov); +} + +fn simple_addosi4(a: i32, b: i32, overflow: *c_int) i32 { + overflow.* = 0; + const min: i32 = -2147483648; + const max: i32 = 2147483647; + if (((a > 0) and (b > max - a)) or + ((a < 0) and (b < min - a))) + overflow.* = 1; + return a +% b; +} + +test "addosi4" { + // -2^31 <= i32 <= 2^31-1 + // 2^31 = 2147483648 + // 2^31-1 = 2147483647 + const min: i32 = -2147483648; + const max: i32 = 2147483647; + var i: i32 = 1; + while (i < max) : (i *|= 2) { + try test__addosi4(i, i); + try test__addosi4(-i, -i); + try test__addosi4(i, -i); + try test__addosi4(-i, i); + } + + // edge cases + // 0 + 0 = 0 + // MIN + MIN overflow + // MAX + MAX overflow + // 0 + MIN MIN + // 0 + MAX MAX + // MIN + 0 MIN + // MAX + 0 MAX + // MIN + MAX -1 + // MAX + MIN -1 + try test__addosi4(0, 0); + try test__addosi4(min, min); + try test__addosi4(max, max); + try test__addosi4(0, min); + try test__addosi4(0, max); + try test__addosi4(min, 0); + try test__addosi4(max, 0); + try test__addosi4(min, max); + try test__addosi4(max, min); + + // derived edge cases + // MIN+1 + MIN overflow + // MAX-1 + MAX overflow + // 1 + MIN = MIN+1 + // -1 + MIN overflow + // -1 + MAX = MAX-1 + // +1 + MAX overflow + // MIN + 1 = MIN+1 + // MIN + -1 overflow + // MAX + 1 overflow + // MAX + -1 = MAX-1 + try test__addosi4(min + 1, min); + try test__addosi4(max - 1, max); + try test__addosi4(1, min); + try test__addosi4(-1, min); + try test__addosi4(-1, max); + try test__addosi4(1, max); + try test__addosi4(min, 1); + try test__addosi4(min, -1); + try test__addosi4(max, -1); + try test__addosi4(max, 1); +} diff --git a/lib/std/special/compiler_rt/addoti4_test.zig b/lib/std/special/compiler_rt/addoti4_test.zig new file mode 100644 index 0000000000..dd0f4e3d3c --- /dev/null +++ b/lib/std/special/compiler_rt/addoti4_test.zig @@ -0,0 +1,77 @@ +const addv = @import("addo.zig"); +const std = @import("std"); +const testing = std.testing; +const math = std.math; + +fn test__addoti4(a: i128, b: i128) !void { + var result_ov: c_int = undefined; + var expected_ov: c_int = undefined; + var result = addv.__addoti4(a, b, &result_ov); + var expected: i128 = simple_addoti4(a, b, &expected_ov); + try testing.expectEqual(expected, result); + try testing.expectEqual(expected_ov, result_ov); +} + +fn simple_addoti4(a: i128, b: i128, overflow: *c_int) i128 { + overflow.* = 0; + const min: i128 = math.minInt(i128); + const max: i128 = math.maxInt(i128); + if (((a > 0) and (b > max - a)) or + ((a < 0) and (b < min - a))) + overflow.* = 1; + return a +% b; +} + +test "addoti4" { + const min: i128 = math.minInt(i128); + const max: i128 = math.maxInt(i128); + var i: i128 = 1; + while (i < max) : (i *|= 2) { + try test__addoti4(i, i); + try test__addoti4(-i, -i); + try test__addoti4(i, -i); + try test__addoti4(-i, i); + } + + // edge cases + // 0 + 0 = 0 + // MIN + MIN overflow + // MAX + MAX overflow + // 0 + MIN MIN + // 0 + MAX MAX + // MIN + 0 MIN + // MAX + 0 MAX + // MIN + MAX -1 + // MAX + MIN -1 + try test__addoti4(0, 0); + try test__addoti4(min, min); + try test__addoti4(max, max); + try test__addoti4(0, min); + try test__addoti4(0, max); + try test__addoti4(min, 0); + try test__addoti4(max, 0); + try test__addoti4(min, max); + try test__addoti4(max, min); + + // derived edge cases + // MIN+1 + MIN overflow + // MAX-1 + MAX overflow + // 1 + MIN = MIN+1 + // -1 + MIN overflow + // -1 + MAX = MAX-1 + // +1 + MAX overflow + // MIN + 1 = MIN+1 + // MIN + -1 overflow + // MAX + 1 overflow + // MAX + -1 = MAX-1 + try test__addoti4(min + 1, min); + try test__addoti4(max - 1, max); + try test__addoti4(1, min); + try test__addoti4(-1, min); + try test__addoti4(-1, max); + try test__addoti4(1, max); + try test__addoti4(min, 1); + try test__addoti4(min, -1); + try test__addoti4(max, -1); + try test__addoti4(max, 1); +} diff --git a/lib/std/special/compiler_rt/mulo.zig b/lib/std/special/compiler_rt/mulo.zig index df4c98134c..78590e5ce1 100644 --- a/lib/std/special/compiler_rt/mulo.zig +++ b/lib/std/special/compiler_rt/mulo.zig @@ -3,7 +3,7 @@ const std = @import("std"); const math = std.math; // mulo - multiplication overflow -// * return a*b. +// * return a*%b. // * return if a*b overflows => 1 else => 0 // - muloXi4_genericSmall as default // - muloXi4_genericFast for 2*bitsize <= usize