authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-04 12:21:31-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-04 12:23:49-07:00
logac2333ee63b3c0af8a075d325e2313c9b255a46e
tree8e5d07a4aaa946c1c681428adafde533d58f5c59
parenta28f2e0dd2d7b78464115bca4968f7c0befa6b28

stage2: fix Type max/min int calculation

This was an attempt to move saturating_arithmetic.zig to the "passing for stage2" section, which did not pan out due to the discovery of 2 prerequisite items that need to be done, but I did make a bug fix along the way of the calculation of max/min integers. This commit also simplifies the saturating arithmetic behavior tests to depend on less of the zig language that is not related to saturating arithmetic.

4 files changed, 138 insertions(+), 96 deletions(-)

src/type.zig+11-8
......@@ -3096,7 +3096,7 @@ pub const Type = extern union {
30963096 return Value.initTag(.zero);
30973097 }
30983098
3099 if ((info.bits - 1) <= std.math.maxInt(u6)) {
3099 if (info.bits <= 6) {
31003100 const n: i64 = -(@as(i64, 1) << @truncate(u6, info.bits - 1));
31013101 return Value.Tag.int_i64.create(arena, n);
31023102 }
......@@ -3117,13 +3117,16 @@ pub const Type = extern union {
31173117 assert(self.zigTypeTag() == .Int);
31183118 const info = self.intInfo(target);
31193119
3120 if (info.signedness == .signed and (info.bits - 1) <= std.math.maxInt(u6)) {
3121 const n: i64 = (@as(i64, 1) << @truncate(u6, info.bits - 1)) - 1;
3122 return Value.Tag.int_i64.create(arena, n);
3123 } else if (info.signedness == .signed and info.bits <= std.math.maxInt(u6)) {
3124 const n: u64 = (@as(u64, 1) << @truncate(u6, info.bits)) - 1;
3125 return Value.Tag.int_u64.create(arena, n);
3126 }
3120 if (info.bits <= 6) switch (info.signedness) {
3121 .signed => {
3122 const n: i64 = (@as(i64, 1) << @truncate(u6, info.bits - 1)) - 1;
3123 return Value.Tag.int_i64.create(arena, n);
3124 },
3125 .unsigned => {
3126 const n: u64 = (@as(u64, 1) << @truncate(u6, info.bits)) - 1;
3127 return Value.Tag.int_u64.create(arena, n);
3128 },
3129 };
31273130
31283131 var res = try std.math.big.int.Managed.init(arena);
31293132 try res.setTwosCompIntLimit(.max, info.signedness, info.bits);
test/behavior.zig+7-2
......@@ -34,8 +34,8 @@ test {
3434 _ = @import("behavior/underscore.zig");
3535 _ = @import("behavior/union.zig");
3636 _ = @import("behavior/usingnamespace.zig");
37 _ = @import("behavior/widening.zig");
3837 _ = @import("behavior/while.zig");
38 _ = @import("behavior/widening.zig");
3939
4040 if (builtin.zig_is_stage2) {
4141 // When all comptime_memory.zig tests pass, #9646 can be closed.
......@@ -140,7 +140,12 @@ test {
140140 _ = @import("behavior/pub_enum.zig");
141141 _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig");
142142 _ = @import("behavior/reflection.zig");
143 _ = @import("behavior/saturating_arithmetic.zig");
143 {
144 // Checklist for getting saturating_arithmetic.zig passing for stage2:
145 // * add __muloti4 to compiler-rt
146 // * implement comptime saturating shift-left
147 _ = @import("behavior/saturating_arithmetic.zig");
148 }
144149 _ = @import("behavior/shuffle.zig");
145150 _ = @import("behavior/select.zig");
146151 _ = @import("behavior/sizeof_and_typeof_stage1.zig");
test/behavior/saturating_arithmetic.zig+70-86
......@@ -1,58 +1,32 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const mem = std.mem;
4const expectEqual = std.testing.expectEqual;
5const Vector = std.meta.Vector;
63const minInt = std.math.minInt;
74const maxInt = std.math.maxInt;
8
9const Op = enum { add, sub, mul, shl };
10fn testSaturatingOp(comptime op: Op, comptime T: type, test_data: [3]T) !void {
11 const a = test_data[0];
12 const b = test_data[1];
13 const expected = test_data[2];
14 {
15 const actual = switch (op) {
16 .add => a +| b,
17 .sub => a -| b,
18 .mul => a *| b,
19 .shl => a <<| b,
20 };
21 try expectEqual(expected, actual);
22 }
23 {
24 var actual = a;
25 switch (op) {
26 .add => actual +|= b,
27 .sub => actual -|= b,
28 .mul => actual *|= b,
29 .shl => actual <<|= b,
30 }
31 try expectEqual(expected, actual);
32 }
33}
5const expect = std.testing.expect;
346
357test "saturating add" {
368 const S = struct {
379 fn doTheTest() !void {
38 // .{a, b, expected a+b}
39 try testSaturatingOp(.add, i8, .{ -3, 10, 7 });
40 try testSaturatingOp(.add, i8, .{ -128, -128, -128 });
41 try testSaturatingOp(.add, i2, .{ 1, 1, 1 });
42 try testSaturatingOp(.add, i64, .{ maxInt(i64), 1, maxInt(i64) });
43 try testSaturatingOp(.add, i128, .{ maxInt(i128), -maxInt(i128), 0 });
44 try testSaturatingOp(.add, i128, .{ minInt(i128), maxInt(i128), -1 });
45 try testSaturatingOp(.add, i8, .{ 127, 127, 127 });
46 try testSaturatingOp(.add, u8, .{ 3, 10, 13 });
47 try testSaturatingOp(.add, u8, .{ 255, 255, 255 });
48 try testSaturatingOp(.add, u2, .{ 3, 2, 3 });
49 try testSaturatingOp(.add, u3, .{ 7, 1, 7 });
50 try testSaturatingOp(.add, u128, .{ maxInt(u128), 1, maxInt(u128) });
10 try testSatAdd(i8, -3, 10, 7);
11 try testSatAdd(i8, -128, -128, -128);
12 try testSatAdd(i2, 1, 1, 1);
13 try testSatAdd(i64, maxInt(i64), 1, maxInt(i64));
14 try testSatAdd(i128, maxInt(i128), -maxInt(i128), 0);
15 try testSatAdd(i128, minInt(i128), maxInt(i128), -1);
16 try testSatAdd(i8, 127, 127, 127);
17 try testSatAdd(u8, 3, 10, 13);
18 try testSatAdd(u8, 255, 255, 255);
19 try testSatAdd(u2, 3, 2, 3);
20 try testSatAdd(u3, 7, 1, 7);
21 try testSatAdd(u128, maxInt(u128), 1, maxInt(u128));
22 }
23
24 fn testSatAdd(comptime T: type, lhs: T, rhs: T, expected: T) !void {
25 try expect((lhs +| rhs) == expected);
5126
52 const u8x3 = std.meta.Vector(3, u8);
53 try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 255, 254, 1 } +| u8x3{ 1, 2, 255 }));
54 const i8x3 = std.meta.Vector(3, i8);
55 try expectEqual(i8x3{ 127, 127, 127 }, (i8x3{ 127, 126, 1 } +| i8x3{ 1, 2, 127 }));
27 var x = lhs;
28 x +|= rhs;
29 try expect(x == expected);
5630 }
5731 };
5832 try S.doTheTest();
......@@ -62,20 +36,24 @@ test "saturating add" {
6236test "saturating subtraction" {
6337 const S = struct {
6438 fn doTheTest() !void {
65 // .{a, b, expected a-b}
66 try testSaturatingOp(.sub, i8, .{ -3, 10, -13 });
67 try testSaturatingOp(.sub, i8, .{ -128, -128, 0 });
68 try testSaturatingOp(.sub, i8, .{ -1, 127, -128 });
69 try testSaturatingOp(.sub, i64, .{ minInt(i64), 1, minInt(i64) });
70 try testSaturatingOp(.sub, i128, .{ maxInt(i128), -1, maxInt(i128) });
71 try testSaturatingOp(.sub, i128, .{ minInt(i128), -maxInt(i128), -1 });
72 try testSaturatingOp(.sub, u8, .{ 10, 3, 7 });
73 try testSaturatingOp(.sub, u8, .{ 0, 255, 0 });
74 try testSaturatingOp(.sub, u5, .{ 0, 31, 0 });
75 try testSaturatingOp(.sub, u128, .{ 0, maxInt(u128), 0 });
39 try testSatSub(i8, -3, 10, -13);
40 try testSatSub(i8, -128, -128, 0);
41 try testSatSub(i8, -1, 127, -128);
42 try testSatSub(i64, minInt(i64), 1, minInt(i64));
43 try testSatSub(i128, maxInt(i128), -1, maxInt(i128));
44 try testSatSub(i128, minInt(i128), -maxInt(i128), -1);
45 try testSatSub(u8, 10, 3, 7);
46 try testSatSub(u8, 0, 255, 0);
47 try testSatSub(u5, 0, 31, 0);
48 try testSatSub(u128, 0, maxInt(u128), 0);
49 }
50
51 fn testSatSub(comptime T: type, lhs: T, rhs: T, expected: T) !void {
52 try expect((lhs -| rhs) == expected);
7653
77 const u8x3 = std.meta.Vector(3, u8);
78 try expectEqual(u8x3{ 0, 0, 0 }, (u8x3{ 0, 0, 0 } -| u8x3{ 255, 255, 255 }));
54 var x = lhs;
55 x -|= rhs;
56 try expect(x == expected);
7957 }
8058 };
8159 try S.doTheTest();
......@@ -84,26 +62,29 @@ test "saturating subtraction" {
8462
8563test "saturating multiplication" {
8664 // TODO: once #9660 has been solved, remove this line
87 if (std.builtin.target.cpu.arch == .wasm32) return error.SkipZigTest;
65 if (builtin.stage2_arch == .wasm32) return error.SkipZigTest;
8866
8967 const S = struct {
9068 fn doTheTest() !void {
91 // .{a, b, expected a*b}
92 try testSaturatingOp(.mul, i8, .{ -3, 10, -30 });
93 try testSaturatingOp(.mul, i4, .{ 2, 4, 7 });
94 try testSaturatingOp(.mul, i8, .{ 2, 127, 127 });
95 // TODO: uncomment these after #9643 has been solved - this should happen at 0.9.0/llvm-13 release
96 // try testSaturatingOp(.mul, i8, .{ -128, -128, 127 });
97 // try testSaturatingOp(.mul, i8, .{ maxInt(i8), maxInt(i8), maxInt(i8) });
98 try testSaturatingOp(.mul, i16, .{ maxInt(i16), -1, minInt(i16) + 1 });
99 try testSaturatingOp(.mul, i128, .{ maxInt(i128), -1, minInt(i128) + 1 });
100 try testSaturatingOp(.mul, i128, .{ minInt(i128), -1, maxInt(i128) });
101 try testSaturatingOp(.mul, u8, .{ 10, 3, 30 });
102 try testSaturatingOp(.mul, u8, .{ 2, 255, 255 });
103 try testSaturatingOp(.mul, u128, .{ maxInt(u128), maxInt(u128), maxInt(u128) });
69 try testSatMul(i8, -3, 10, -30);
70 try testSatMul(i4, 2, 4, 7);
71 try testSatMul(i8, 2, 127, 127);
72 try testSatMul(i8, -128, -128, 127);
73 try testSatMul(i8, maxInt(i8), maxInt(i8), maxInt(i8));
74 try testSatMul(i16, maxInt(i16), -1, minInt(i16) + 1);
75 try testSatMul(i128, maxInt(i128), -1, minInt(i128) + 1);
76 try testSatMul(i128, minInt(i128), -1, maxInt(i128));
77 try testSatMul(u8, 10, 3, 30);
78 try testSatMul(u8, 2, 255, 255);
79 try testSatMul(u128, maxInt(u128), maxInt(u128), maxInt(u128));
80 }
10481
105 const u8x3 = std.meta.Vector(3, u8);
106 try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 2, 2, 2 } *| u8x3{ 255, 255, 255 }));
82 fn testSatMul(comptime T: type, lhs: T, rhs: T, expected: T) !void {
83 try expect((lhs *| rhs) == expected);
84
85 var x = lhs;
86 x *|= rhs;
87 try expect(x == expected);
10788 }
10889 };
10990
......@@ -114,21 +95,24 @@ test "saturating multiplication" {
11495test "saturating shift-left" {
11596 const S = struct {
11697 fn doTheTest() !void {
117 // .{a, b, expected a<<b}
118 try testSaturatingOp(.shl, i8, .{ 1, 2, 4 });
119 try testSaturatingOp(.shl, i8, .{ 127, 1, 127 });
120 try testSaturatingOp(.shl, i8, .{ -128, 1, -128 });
98 try testSatShl(i8, 1, 2, 4);
99 try testSatShl(i8, 127, 1, 127);
100 try testSatShl(i8, -128, 1, -128);
121101 // TODO: remove this check once #9668 is completed
122 if (std.builtin.target.cpu.arch != .wasm32) {
102 if (builtin.stage2_arch != .wasm32) {
123103 // skip testing ints > 64 bits on wasm due to miscompilation / wasmtime ci error
124 try testSaturatingOp(.shl, i128, .{ maxInt(i128), 64, maxInt(i128) });
125 try testSaturatingOp(.shl, u128, .{ maxInt(u128), 64, maxInt(u128) });
104 try testSatShl(i128, maxInt(i128), 64, maxInt(i128));
105 try testSatShl(u128, maxInt(u128), 64, maxInt(u128));
126106 }
127 try testSaturatingOp(.shl, u8, .{ 1, 2, 4 });
128 try testSaturatingOp(.shl, u8, .{ 255, 1, 255 });
107 try testSatShl(u8, 1, 2, 4);
108 try testSatShl(u8, 255, 1, 255);
109 }
110 fn testSatShl(comptime T: type, lhs: T, rhs: T, expected: T) !void {
111 try expect((lhs <<| rhs) == expected);
129112
130 const u8x3 = std.meta.Vector(3, u8);
131 try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 255, 255, 255 } <<| u8x3{ 1, 1, 1 }));
113 var x = lhs;
114 x <<|= rhs;
115 try expect(x == expected);
132116 }
133117 };
134118 try S.doTheTest();
test/behavior/vector.zig+50
......@@ -647,3 +647,53 @@ test "mask parameter of @shuffle is comptime scope" {
647647 });
648648 _ = shuffled;
649649}
650
651test "saturating add" {
652 const S = struct {
653 fn doTheTest() !void {
654 const u8x3 = std.meta.Vector(3, u8);
655 try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 255, 254, 1 } +| u8x3{ 1, 2, 255 }));
656 const i8x3 = std.meta.Vector(3, i8);
657 try expectEqual(i8x3{ 127, 127, 127 }, (i8x3{ 127, 126, 1 } +| i8x3{ 1, 2, 127 }));
658 }
659 };
660 try S.doTheTest();
661 comptime try S.doTheTest();
662}
663
664test "saturating subtraction" {
665 const S = struct {
666 fn doTheTest() !void {
667 const u8x3 = std.meta.Vector(3, u8);
668 try expectEqual(u8x3{ 0, 0, 0 }, (u8x3{ 0, 0, 0 } -| u8x3{ 255, 255, 255 }));
669 }
670 };
671 try S.doTheTest();
672 comptime try S.doTheTest();
673}
674
675test "saturating multiplication" {
676 // TODO: once #9660 has been solved, remove this line
677 if (std.builtin.target.cpu.arch == .wasm32) return error.SkipZigTest;
678
679 const S = struct {
680 fn doTheTest() !void {
681 const u8x3 = std.meta.Vector(3, u8);
682 try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 2, 2, 2 } *| u8x3{ 255, 255, 255 }));
683 }
684 };
685
686 try S.doTheTest();
687 comptime try S.doTheTest();
688}
689
690test "saturating shift-left" {
691 const S = struct {
692 fn doTheTest() !void {
693 const u8x3 = std.meta.Vector(3, u8);
694 try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 255, 255, 255 } <<| u8x3{ 1, 1, 1 }));
695 }
696 };
697 try S.doTheTest();
698 comptime try S.doTheTest();
699}