authorgravatar for matu3ba@users.noreply.github.commatu3ba <matu3ba@users.noreply.github.com> 2022-02-07 19:27:21+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-02-07 13:27:21-05:00
log3db130ff3d8175adce610f7805a149810cf7989d
treee9e330f1efdd587ef45be88b6f175447c32ad82f
parente382e7be2b7a4c40a8db78dca0de38141dad8c67
signature Signed by PGP key 4AEE18F83AFDEB23

compiler_rt: add addo (#10824)

- approach by Hacker's Delight with wrapping addition - ca. 1.10x perf over the standard approach on my laptop - tests with all combinations of min,max with -1,0,+1 and combinations of sequences +-1,2,4..,max

6 files changed, 277 insertions(+), 1 deletions(-)

lib/std/special/compiler_rt.zig+6
...@@ -106,6 +106,12 @@ comptime {...@@ -106,6 +106,12 @@ comptime {
106 }106 }
107107
108 // Integral arithmetic which returns if overflow108 // Integral arithmetic which returns if overflow
109 const __addosi4 = @import("compiler_rt/addo.zig").__addosi4;
110 @export(__addosi4, .{ .name = "__addosi4", .linkage = linkage });
111 const __addodi4 = @import("compiler_rt/addo.zig").__addodi4;
112 @export(__addodi4, .{ .name = "__addodi4", .linkage = linkage });
113 const __addoti4 = @import("compiler_rt/addo.zig").__addoti4;
114 @export(__addoti4, .{ .name = "__addoti4", .linkage = linkage });
109 const __mulosi4 = @import("compiler_rt/mulo.zig").__mulosi4;115 const __mulosi4 = @import("compiler_rt/mulo.zig").__mulosi4;
110 @export(__mulosi4, .{ .name = "__mulosi4", .linkage = linkage });116 @export(__mulosi4, .{ .name = "__mulosi4", .linkage = linkage });
111 const __mulodi4 = @import("compiler_rt/mulo.zig").__mulodi4;117 const __mulodi4 = @import("compiler_rt/mulo.zig").__mulodi4;
lib/std/special/compiler_rt/addo.zig created+38
...@@ -0,0 +1,38 @@
1const builtin = @import("builtin");
2
3// addo - add overflow
4// * return a+%b.
5// * return if a+b overflows => 1 else => 0
6// - addoXi4_generic as default
7
8inline fn addoXi4_generic(comptime ST: type, a: ST, b: ST, overflow: *c_int) ST {
9 @setRuntimeSafety(builtin.is_test);
10 overflow.* = 0;
11 var sum: ST = a +% b;
12 // Hackers Delight: section Overflow Detection, subsection Signed Add/Subtract
13 // Let sum = a +% b == a + b + carry == wraparound addition.
14 // Overflow in a+b+carry occurs, iff a and b have opposite signs
15 // and the sign of a+b+carry is the same as a (or equivalently b).
16 // Slower routine: res = ~(a ^ b) & ((sum ^ a)
17 // Faster routine: res = (sum ^ a) & (sum ^ b)
18 // Oerflow occured, iff (res < 0)
19 if (((sum ^ a) & (sum ^ b)) < 0)
20 overflow.* = 1;
21 return sum;
22}
23
24pub fn __addosi4(a: i32, b: i32, overflow: *c_int) callconv(.C) i32 {
25 return addoXi4_generic(i32, a, b, overflow);
26}
27pub fn __addodi4(a: i64, b: i64, overflow: *c_int) callconv(.C) i64 {
28 return addoXi4_generic(i64, a, b, overflow);
29}
30pub fn __addoti4(a: i128, b: i128, overflow: *c_int) callconv(.C) i128 {
31 return addoXi4_generic(i128, a, b, overflow);
32}
33
34test {
35 _ = @import("addosi4_test.zig");
36 _ = @import("addodi4_test.zig");
37 _ = @import("addoti4_test.zig");
38}
lib/std/special/compiler_rt/addodi4_test.zig created+77
...@@ -0,0 +1,77 @@
1const addv = @import("addo.zig");
2const std = @import("std");
3const testing = std.testing;
4const math = std.math;
5
6fn test__addodi4(a: i64, b: i64) !void {
7 var result_ov: c_int = undefined;
8 var expected_ov: c_int = undefined;
9 var result = addv.__addodi4(a, b, &result_ov);
10 var expected: i64 = simple_addodi4(a, b, &expected_ov);
11 try testing.expectEqual(expected, result);
12 try testing.expectEqual(expected_ov, result_ov);
13}
14
15fn simple_addodi4(a: i64, b: i64, overflow: *c_int) i64 {
16 overflow.* = 0;
17 const min: i64 = math.minInt(i64);
18 const max: i64 = math.maxInt(i64);
19 if (((a > 0) and (b > max - a)) or
20 ((a < 0) and (b < min - a)))
21 overflow.* = 1;
22 return a +% b;
23}
24
25test "addodi4" {
26 const min: i64 = math.minInt(i64);
27 const max: i64 = math.maxInt(i64);
28 var i: i64 = 1;
29 while (i < max) : (i *|= 2) {
30 try test__addodi4(i, i);
31 try test__addodi4(-i, -i);
32 try test__addodi4(i, -i);
33 try test__addodi4(-i, i);
34 }
35
36 // edge cases
37 // 0 + 0 = 0
38 // MIN + MIN overflow
39 // MAX + MAX overflow
40 // 0 + MIN MIN
41 // 0 + MAX MAX
42 // MIN + 0 MIN
43 // MAX + 0 MAX
44 // MIN + MAX -1
45 // MAX + MIN -1
46 try test__addodi4(0, 0);
47 try test__addodi4(min, min);
48 try test__addodi4(max, max);
49 try test__addodi4(0, min);
50 try test__addodi4(0, max);
51 try test__addodi4(min, 0);
52 try test__addodi4(max, 0);
53 try test__addodi4(min, max);
54 try test__addodi4(max, min);
55
56 // derived edge cases
57 // MIN+1 + MIN overflow
58 // MAX-1 + MAX overflow
59 // 1 + MIN = MIN+1
60 // -1 + MIN overflow
61 // -1 + MAX = MAX-1
62 // +1 + MAX overflow
63 // MIN + 1 = MIN+1
64 // MIN + -1 overflow
65 // MAX + 1 overflow
66 // MAX + -1 = MAX-1
67 try test__addodi4(min + 1, min);
68 try test__addodi4(max - 1, max);
69 try test__addodi4(1, min);
70 try test__addodi4(-1, min);
71 try test__addodi4(-1, max);
72 try test__addodi4(1, max);
73 try test__addodi4(min, 1);
74 try test__addodi4(min, -1);
75 try test__addodi4(max, -1);
76 try test__addodi4(max, 1);
77}
lib/std/special/compiler_rt/addosi4_test.zig created+78
...@@ -0,0 +1,78 @@
1const addv = @import("addo.zig");
2const testing = @import("std").testing;
3
4fn test__addosi4(a: i32, b: i32) !void {
5 var result_ov: c_int = undefined;
6 var expected_ov: c_int = undefined;
7 var result = addv.__addosi4(a, b, &result_ov);
8 var expected: i32 = simple_addosi4(a, b, &expected_ov);
9 try testing.expectEqual(expected, result);
10 try testing.expectEqual(expected_ov, result_ov);
11}
12
13fn simple_addosi4(a: i32, b: i32, overflow: *c_int) i32 {
14 overflow.* = 0;
15 const min: i32 = -2147483648;
16 const max: i32 = 2147483647;
17 if (((a > 0) and (b > max - a)) or
18 ((a < 0) and (b < min - a)))
19 overflow.* = 1;
20 return a +% b;
21}
22
23test "addosi4" {
24 // -2^31 <= i32 <= 2^31-1
25 // 2^31 = 2147483648
26 // 2^31-1 = 2147483647
27 const min: i32 = -2147483648;
28 const max: i32 = 2147483647;
29 var i: i32 = 1;
30 while (i < max) : (i *|= 2) {
31 try test__addosi4(i, i);
32 try test__addosi4(-i, -i);
33 try test__addosi4(i, -i);
34 try test__addosi4(-i, i);
35 }
36
37 // edge cases
38 // 0 + 0 = 0
39 // MIN + MIN overflow
40 // MAX + MAX overflow
41 // 0 + MIN MIN
42 // 0 + MAX MAX
43 // MIN + 0 MIN
44 // MAX + 0 MAX
45 // MIN + MAX -1
46 // MAX + MIN -1
47 try test__addosi4(0, 0);
48 try test__addosi4(min, min);
49 try test__addosi4(max, max);
50 try test__addosi4(0, min);
51 try test__addosi4(0, max);
52 try test__addosi4(min, 0);
53 try test__addosi4(max, 0);
54 try test__addosi4(min, max);
55 try test__addosi4(max, min);
56
57 // derived edge cases
58 // MIN+1 + MIN overflow
59 // MAX-1 + MAX overflow
60 // 1 + MIN = MIN+1
61 // -1 + MIN overflow
62 // -1 + MAX = MAX-1
63 // +1 + MAX overflow
64 // MIN + 1 = MIN+1
65 // MIN + -1 overflow
66 // MAX + 1 overflow
67 // MAX + -1 = MAX-1
68 try test__addosi4(min + 1, min);
69 try test__addosi4(max - 1, max);
70 try test__addosi4(1, min);
71 try test__addosi4(-1, min);
72 try test__addosi4(-1, max);
73 try test__addosi4(1, max);
74 try test__addosi4(min, 1);
75 try test__addosi4(min, -1);
76 try test__addosi4(max, -1);
77 try test__addosi4(max, 1);
78}
lib/std/special/compiler_rt/addoti4_test.zig created+77
...@@ -0,0 +1,77 @@
1const addv = @import("addo.zig");
2const std = @import("std");
3const testing = std.testing;
4const math = std.math;
5
6fn test__addoti4(a: i128, b: i128) !void {
7 var result_ov: c_int = undefined;
8 var expected_ov: c_int = undefined;
9 var result = addv.__addoti4(a, b, &result_ov);
10 var expected: i128 = simple_addoti4(a, b, &expected_ov);
11 try testing.expectEqual(expected, result);
12 try testing.expectEqual(expected_ov, result_ov);
13}
14
15fn simple_addoti4(a: i128, b: i128, overflow: *c_int) i128 {
16 overflow.* = 0;
17 const min: i128 = math.minInt(i128);
18 const max: i128 = math.maxInt(i128);
19 if (((a > 0) and (b > max - a)) or
20 ((a < 0) and (b < min - a)))
21 overflow.* = 1;
22 return a +% b;
23}
24
25test "addoti4" {
26 const min: i128 = math.minInt(i128);
27 const max: i128 = math.maxInt(i128);
28 var i: i128 = 1;
29 while (i < max) : (i *|= 2) {
30 try test__addoti4(i, i);
31 try test__addoti4(-i, -i);
32 try test__addoti4(i, -i);
33 try test__addoti4(-i, i);
34 }
35
36 // edge cases
37 // 0 + 0 = 0
38 // MIN + MIN overflow
39 // MAX + MAX overflow
40 // 0 + MIN MIN
41 // 0 + MAX MAX
42 // MIN + 0 MIN
43 // MAX + 0 MAX
44 // MIN + MAX -1
45 // MAX + MIN -1
46 try test__addoti4(0, 0);
47 try test__addoti4(min, min);
48 try test__addoti4(max, max);
49 try test__addoti4(0, min);
50 try test__addoti4(0, max);
51 try test__addoti4(min, 0);
52 try test__addoti4(max, 0);
53 try test__addoti4(min, max);
54 try test__addoti4(max, min);
55
56 // derived edge cases
57 // MIN+1 + MIN overflow
58 // MAX-1 + MAX overflow
59 // 1 + MIN = MIN+1
60 // -1 + MIN overflow
61 // -1 + MAX = MAX-1
62 // +1 + MAX overflow
63 // MIN + 1 = MIN+1
64 // MIN + -1 overflow
65 // MAX + 1 overflow
66 // MAX + -1 = MAX-1
67 try test__addoti4(min + 1, min);
68 try test__addoti4(max - 1, max);
69 try test__addoti4(1, min);
70 try test__addoti4(-1, min);
71 try test__addoti4(-1, max);
72 try test__addoti4(1, max);
73 try test__addoti4(min, 1);
74 try test__addoti4(min, -1);
75 try test__addoti4(max, -1);
76 try test__addoti4(max, 1);
77}
lib/std/special/compiler_rt/mulo.zig+1-1
...@@ -3,7 +3,7 @@ const std = @import("std");...@@ -3,7 +3,7 @@ const std = @import("std");
3const math = std.math;3const math = std.math;
44
5// mulo - multiplication overflow5// mulo - multiplication overflow
6// * return a*b.6// * return a*%b.
7// * return if a*b overflows => 1 else => 07// * return if a*b overflows => 1 else => 0
8// - muloXi4_genericSmall as default8// - muloXi4_genericSmall as default
9// - muloXi4_genericFast for 2*bitsize <= usize9// - muloXi4_genericFast for 2*bitsize <= usize