authorgravatar for jan.hafer@rwth-aachen.deJan Philipp Hafer <jan.hafer@rwth-aachen.de> 2022-02-07 00:20:13+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-08 02:14:29-05:00
logfc59a0406157dbd0704cf9f05cd04b6a8c87d7df
tree828950c75e372509e67f607a2588bd22c3b6a1fb
parenta15d2d582b4015af0f72caa2b5f09b7e665e2c1e

compiler_rt: add subo

- approach by Hacker's Delight with wrapping subtraction - performance expected to be similar to addo - tests with all relevant combinations of min,max with -1,0,+1 and all combinations of sequences +-1,2,4..,max

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

lib/std/special/compiler_rt.zig+6
...@@ -112,6 +112,12 @@ comptime {...@@ -112,6 +112,12 @@ comptime {
112 @export(__addodi4, .{ .name = "__addodi4", .linkage = linkage });112 @export(__addodi4, .{ .name = "__addodi4", .linkage = linkage });
113 const __addoti4 = @import("compiler_rt/addo.zig").__addoti4;113 const __addoti4 = @import("compiler_rt/addo.zig").__addoti4;
114 @export(__addoti4, .{ .name = "__addoti4", .linkage = linkage });114 @export(__addoti4, .{ .name = "__addoti4", .linkage = linkage });
115 const __subosi4 = @import("compiler_rt/subo.zig").__subosi4;
116 @export(__subosi4, .{ .name = "__subosi4", .linkage = linkage });
117 const __subodi4 = @import("compiler_rt/subo.zig").__subodi4;
118 @export(__subodi4, .{ .name = "__subodi4", .linkage = linkage });
119 const __suboti4 = @import("compiler_rt/subo.zig").__suboti4;
120 @export(__suboti4, .{ .name = "__suboti4", .linkage = linkage });
115 const __mulosi4 = @import("compiler_rt/mulo.zig").__mulosi4;121 const __mulosi4 = @import("compiler_rt/mulo.zig").__mulosi4;
116 @export(__mulosi4, .{ .name = "__mulosi4", .linkage = linkage });122 @export(__mulosi4, .{ .name = "__mulosi4", .linkage = linkage });
117 const __mulodi4 = @import("compiler_rt/mulo.zig").__mulodi4;123 const __mulodi4 = @import("compiler_rt/mulo.zig").__mulodi4;
lib/std/special/compiler_rt/addo.zig+1-1
...@@ -15,7 +15,7 @@ inline fn addoXi4_generic(comptime ST: type, a: ST, b: ST, overflow: *c_int) ST...@@ -15,7 +15,7 @@ inline fn addoXi4_generic(comptime ST: type, a: ST, b: ST, overflow: *c_int) ST
15 // and the sign of a+b+carry is the same as a (or equivalently b).15 // and the sign of a+b+carry is the same as a (or equivalently b).
16 // Slower routine: res = ~(a ^ b) & ((sum ^ a)16 // Slower routine: res = ~(a ^ b) & ((sum ^ a)
17 // Faster routine: res = (sum ^ a) & (sum ^ b)17 // Faster routine: res = (sum ^ a) & (sum ^ b)
18 // Oerflow occured, iff (res < 0)18 // Overflow occured, iff (res < 0)
19 if (((sum ^ a) & (sum ^ b)) < 0)19 if (((sum ^ a) & (sum ^ b)) < 0)
20 overflow.* = 1;20 overflow.* = 1;
21 return sum;21 return sum;
lib/std/special/compiler_rt/subo.zig created+38
...@@ -0,0 +1,38 @@
1const builtin = @import("builtin");
2
3// subo - subtract overflow
4// * return a-%b.
5// * return if a-b overflows => 1 else => 0
6// - suboXi4_generic as default
7
8inline fn suboXi4_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 subtraction.
14 // Overflow in a-b-carry occurs, iff a and b have opposite signs
15 // and the sign of a-b-carry is opposite of a (or equivalently same as b).
16 // Faster routine: res = (a ^ b) & (sum ^ a)
17 // Slower routine: res = (sum^a) & ~(sum^b)
18 // Overflow occured, iff (res < 0)
19 if (((a ^ b) & (sum ^ a)) < 0)
20 overflow.* = 1;
21 return sum;
22}
23
24pub fn __subosi4(a: i32, b: i32, overflow: *c_int) callconv(.C) i32 {
25 return suboXi4_generic(i32, a, b, overflow);
26}
27pub fn __subodi4(a: i64, b: i64, overflow: *c_int) callconv(.C) i64 {
28 return suboXi4_generic(i64, a, b, overflow);
29}
30pub fn __suboti4(a: i128, b: i128, overflow: *c_int) callconv(.C) i128 {
31 return suboXi4_generic(i128, a, b, overflow);
32}
33
34test {
35 _ = @import("subosi4_test.zig");
36 _ = @import("subodi4_test.zig");
37 _ = @import("suboti4_test.zig");
38}
lib/std/special/compiler_rt/subodi4_test.zig created+81
...@@ -0,0 +1,81 @@
1const subo = @import("subo.zig");
2const std = @import("std");
3const testing = std.testing;
4const math = std.math;
5
6fn test__subodi4(a: i64, b: i64) !void {
7 var result_ov: c_int = undefined;
8 var expected_ov: c_int = undefined;
9 var result = subo.__subodi4(a, b, &result_ov);
10 var expected: i64 = simple_subodi4(a, b, &expected_ov);
11 try testing.expectEqual(expected, result);
12 try testing.expectEqual(expected_ov, result_ov);
13}
14
15// 2 cases on evaluating `a-b`:
16// 1. `a-b` may underflow, iff b>0 && a<0 and a-b < min <=> a<min+b
17// 2. `a-b` may overflow, iff b<0 && a>0 and a-b > max <=> a>max+b
18// `-b` evaluation may overflow, iff b==min, but this is handled by the hardware
19pub fn simple_subodi4(a: i64, b: i64, overflow: *c_int) i64 {
20 overflow.* = 0;
21 const min: i64 = math.minInt(i64);
22 const max: i64 = math.maxInt(i64);
23 if (((b > 0) and (a < min + b)) or
24 ((b < 0) and (a > max + b)))
25 overflow.* = 1;
26 return a -% b;
27}
28
29test "subodi3" {
30 const min: i64 = math.minInt(i64);
31 const max: i64 = math.maxInt(i64);
32 var i: i64 = 1;
33 while (i < max) : (i *|= 2) {
34 try test__subodi4(i, i);
35 try test__subodi4(-i, -i);
36 try test__subodi4(i, -i);
37 try test__subodi4(-i, i);
38 }
39
40 // edge cases
41 // 0 - 0 = 0
42 // MIN - MIN = 0
43 // MAX - MAX = 0
44 // 0 - MIN overflow
45 // 0 - MAX = MIN+1
46 // MIN - 0 = MIN
47 // MAX - 0 = MAX
48 // MIN - MAX overflow
49 // MAX - MIN overflow
50 try test__subodi4(0, 0);
51 try test__subodi4(min, min);
52 try test__subodi4(max, max);
53 try test__subodi4(0, min);
54 try test__subodi4(0, max);
55 try test__subodi4(min, 0);
56 try test__subodi4(max, 0);
57 try test__subodi4(min, max);
58 try test__subodi4(max, min);
59
60 // derived edge cases
61 // MIN+1 - MIN = 1
62 // MAX-1 - MAX = -1
63 // 1 - MIN overflow
64 // -1 - MIN = MAX
65 // -1 - MAX = MIN
66 // +1 - MAX = MIN+2
67 // MIN - 1 overflow
68 // MIN - -1 = MIN+1
69 // MAX - 1 = MAX-1
70 // MAX - -1 overflow
71 try test__subodi4(min + 1, min);
72 try test__subodi4(max - 1, max);
73 try test__subodi4(1, min);
74 try test__subodi4(-1, min);
75 try test__subodi4(-1, max);
76 try test__subodi4(1, max);
77 try test__subodi4(min, 1);
78 try test__subodi4(min, -1);
79 try test__subodi4(max, -1);
80 try test__subodi4(max, 1);
81}
lib/std/special/compiler_rt/subosi4_test.zig created+82
...@@ -0,0 +1,82 @@
1const subo = @import("subo.zig");
2const testing = @import("std").testing;
3
4fn test__subosi4(a: i32, b: i32) !void {
5 var result_ov: c_int = undefined;
6 var expected_ov: c_int = undefined;
7 var result = subo.__subosi4(a, b, &result_ov);
8 var expected: i32 = simple_subosi4(a, b, &expected_ov);
9 try testing.expectEqual(expected, result);
10 try testing.expectEqual(expected_ov, result_ov);
11}
12
13// 2 cases on evaluating `a-b`:
14// 1. `a-b` may underflow, iff b>0 && a<0 and a-b < min <=> a<min+b
15// 2. `a-b` may overflow, iff b<0 && a>0 and a-b > max <=> a>max+b
16// `-b` evaluation may overflow, iff b==min, but this is handled by the hardware
17pub fn simple_subosi4(a: i32, b: i32, overflow: *c_int) i32 {
18 overflow.* = 0;
19 const min: i32 = -2147483648;
20 const max: i32 = 2147483647;
21 if (((b > 0) and (a < min + b)) or
22 ((b < 0) and (a > max + b)))
23 overflow.* = 1;
24 return a -% b;
25}
26
27test "subosi3" {
28 // -2^31 <= i32 <= 2^31-1
29 // 2^31 = 2147483648
30 // 2^31-1 = 2147483647
31 const min: i32 = -2147483648;
32 const max: i32 = 2147483647;
33 var i: i32 = 1;
34 while (i < max) : (i *|= 2) {
35 try test__subosi4(i, i);
36 try test__subosi4(-i, -i);
37 try test__subosi4(i, -i);
38 try test__subosi4(-i, i);
39 }
40
41 // edge cases
42 // 0 - 0 = 0
43 // MIN - MIN = 0
44 // MAX - MAX = 0
45 // 0 - MIN overflow
46 // 0 - MAX = MIN+1
47 // MIN - 0 = MIN
48 // MAX - 0 = MAX
49 // MIN - MAX overflow
50 // MAX - MIN overflow
51 try test__subosi4(0, 0);
52 try test__subosi4(min, min);
53 try test__subosi4(max, max);
54 try test__subosi4(0, min);
55 try test__subosi4(0, max);
56 try test__subosi4(min, 0);
57 try test__subosi4(max, 0);
58 try test__subosi4(min, max);
59 try test__subosi4(max, min);
60
61 // derived edge cases
62 // MIN+1 - MIN = 1
63 // MAX-1 - MAX = -1
64 // 1 - MIN overflow
65 // -1 - MIN = MAX
66 // -1 - MAX = MIN
67 // +1 - MAX = MIN+2
68 // MIN - 1 overflow
69 // MIN - -1 = MIN+1
70 // MAX - 1 = MAX-1
71 // MAX - -1 overflow
72 try test__subosi4(min + 1, min);
73 try test__subosi4(max - 1, max);
74 try test__subosi4(1, min);
75 try test__subosi4(-1, min);
76 try test__subosi4(-1, max);
77 try test__subosi4(1, max);
78 try test__subosi4(min, 1);
79 try test__subosi4(min, -1);
80 try test__subosi4(max, -1);
81 try test__subosi4(max, 1);
82}
lib/std/special/compiler_rt/suboti4_test.zig created+81
...@@ -0,0 +1,81 @@
1const subo = @import("subo.zig");
2const std = @import("std");
3const testing = std.testing;
4const math = std.math;
5
6fn test__suboti4(a: i128, b: i128) !void {
7 var result_ov: c_int = undefined;
8 var expected_ov: c_int = undefined;
9 var result = subo.__suboti4(a, b, &result_ov);
10 var expected: i128 = simple_suboti4(a, b, &expected_ov);
11 try testing.expectEqual(expected, result);
12 try testing.expectEqual(expected_ov, result_ov);
13}
14
15// 2 cases on evaluating `a-b`:
16// 1. `a-b` may underflow, iff b>0 && a<0 and a-b < min <=> a<min+b
17// 2. `a-b` may overflow, iff b<0 && a>0 and a-b > max <=> a>max+b
18// `-b` evaluation may overflow, iff b==min, but this is handled by the hardware
19pub fn simple_suboti4(a: i128, b: i128, overflow: *c_int) i128 {
20 overflow.* = 0;
21 const min: i128 = math.minInt(i128);
22 const max: i128 = math.maxInt(i128);
23 if (((b > 0) and (a < min + b)) or
24 ((b < 0) and (a > max + b)))
25 overflow.* = 1;
26 return a -% b;
27}
28
29test "suboti3" {
30 const min: i128 = math.minInt(i128);
31 const max: i128 = math.maxInt(i128);
32 var i: i128 = 1;
33 while (i < max) : (i *|= 2) {
34 try test__suboti4(i, i);
35 try test__suboti4(-i, -i);
36 try test__suboti4(i, -i);
37 try test__suboti4(-i, i);
38 }
39
40 // edge cases
41 // 0 - 0 = 0
42 // MIN - MIN = 0
43 // MAX - MAX = 0
44 // 0 - MIN overflow
45 // 0 - MAX = MIN+1
46 // MIN - 0 = MIN
47 // MAX - 0 = MAX
48 // MIN - MAX overflow
49 // MAX - MIN overflow
50 try test__suboti4(0, 0);
51 try test__suboti4(min, min);
52 try test__suboti4(max, max);
53 try test__suboti4(0, min);
54 try test__suboti4(0, max);
55 try test__suboti4(min, 0);
56 try test__suboti4(max, 0);
57 try test__suboti4(min, max);
58 try test__suboti4(max, min);
59
60 // derived edge cases
61 // MIN+1 - MIN = 1
62 // MAX-1 - MAX = -1
63 // 1 - MIN overflow
64 // -1 - MIN = MAX
65 // -1 - MAX = MIN
66 // +1 - MAX = MIN+2
67 // MIN - 1 overflow
68 // MIN - -1 = MIN+1
69 // MAX - 1 = MAX-1
70 // MAX - -1 overflow
71 try test__suboti4(min + 1, min);
72 try test__suboti4(max - 1, max);
73 try test__suboti4(1, min);
74 try test__suboti4(-1, min);
75 try test__suboti4(-1, max);
76 try test__suboti4(1, max);
77 try test__suboti4(min, 1);
78 try test__suboti4(min, -1);
79 try test__suboti4(max, -1);
80 try test__suboti4(max, 1);
81}